2014-11-27 10 views
6

Sono nuovo di swift e ho qualche difficoltà a gestire i puntatori di CFString (o NSString) non gestito. Sto lavorando su un progetto CoreMIDI che implica l'utilizzo di UnsafeMutablePointer> come si può vedere in questa funzione:?Swift UnsafeMutablePointer <Unmanaged ?> Allocazione e stampa

func MIDIObjectGetStringProperty(_ obj: MIDIObjectRef, 
          _ propertyID: CFString!, 
          _ str: UnsafeMutablePointer<Unmanaged<CFString>?>) -> OSStatus 

Il mio problema è che voglio per allocare un buffer per ricevere il contenuto della proprietà (_str) quindi chiama la funzione sopra e infine stampa il contenuto nella console utilizzando println.

Al momento ho scritto questo:

// Get the first midi source (I know it exists) 
var midiEndPoint : Unmanaged<MIDIEndpointRef> = MIDIGetSource(0) 

//C reate a "constant" of 256 
let buf = NSMutableData(capacity: 256) 

// Allocate a string buffer of 256 characters (I'm not even sure this does what I want) 
var name = UnsafeMutablePointer<Unmanaged<CFString>?>(buf!.bytes) 

// Call the function to fill the string buffer with the display name of the midi device 
var err : OSStatus = MIDIObjectGetStringProperty(&midiEndPoint,kMIDIPropertyDisplayName,name) 

// Print the string ... here no surprises I don't know what to write to print the content of the pointer, so it prints the address for the moment 
println(name) 

non ho trovato alcun codice di esempio per utilizzare le funzioni CoreMIDI sulla mela developper libreria non su internet. Sono davvero confuso perché vengo da cpp e le cose sono molto diverse in swift.

EDIT:

Dopo le risposte Rintaro e Martin ho ancora un problema, tutta la mia prova sono fatto su iOS 8.1 e se copio il codice è portato a me il compilatore mi dice che non posso scrivere:

let err = MIDIObjectGetStringProperty(midiEndPoint, kMIDIPropertyDisplayName, &property) 

I risultati in "Non gestito" non sono convertibili in "MIDIObjectRef". Quindi ho aggiunto un "&" perché MIDIObjectRef è un UnsafeMutablePointer <vuoto>.

let midiEndPoint = MIDIGetSource(0) 
var property : Unmanaged<CFString>? 
let err = MIDIObjectGetStringProperty(&midiEndPoint, kMIDIPropertyDisplayName, &property) 

Ora: 'No gestito <MIDIEndpoint>' non è convertibile in '@lvalue inout $ T2'. Alla fine ho dovuto cambiare il primo let in var, senza capire perché?!?

var midiEndPoint = MIDIGetSource(0) 
var property : Unmanaged<CFString>? 
let err = MIDIObjectGetStringProperty(&midiEndPoint, kMIDIPropertyDisplayName, &property) 

Il codice ora viene compilato ed eseguito, ma MIDIObjectGetStringProperty torna OSStatus sbagliare -50 che corrisponde alla IOW o da MacErros.h:

paramErr = -50, /*error in user parameter list*/ 

Così sembra che i parametri non sono quelli che MIDIObjectGetStringProperty è aspettando.

La fonte "0" non esiste sul mio iPad perché MIDIGetNumberOfSources() restituisce 1. Ecco il codice completo:

var numDestinations: ItemCount = MIDIGetNumberOfDestinations() 
    println("MIDI Destinations : " + String(numDestinations)) 

    for var i : ItemCount = 0 ; i < numDestinations; ++i{ 
     var midiEndPoint = MIDIGetDestination(i) 

     var property : Unmanaged<CFString>? 
     let err = MIDIObjectGetStringProperty(&midiEndPoint, kMIDIPropertyDisplayName, &property) 
     if err == noErr { 
      let displayName = property!.takeRetainedValue() as String 
      println(displayName) 
     }else{ 
      println("error : "+String(err)) 
     } 
    } 

Displays:

MIDI Destinations : 1 
error : -50 

davvero non capisco niente. ..

UPDATE:

Infine Martin trovato la soluzione, sembra che ci siano due diverse definizioni di MIDIObjectRef in architetture a 32 e 64 bit. Mentre eseguo il codice su un vecchio iPad 2, il mio codice ha cercato di compilare in modalità 32 bit in cui il valore restituito da MIDIGetSource (i) non è convertibile in MIDIObjectRef. La soluzione è quella di "getto pericoloso" il punto finale MIDI su 32 bit architetture:

#if arch(arm64) || arch(x86_64) 
    let midiEndPoint = MIDIGetDestination(i) 
#else 
    let midiEndPoint = unsafeBitCast(MIDIGetDestination(i), MIDIObjectRef.self) 
#endif 

... O acquistare un nuovo dispositivo a 64 bit ...

Grazie per il prezioso aiuto

risposta

7

Non ho esperienza con CoreMIDI e non poteva provarlo, ma questo è come dovrebbe funzionare:

let midiEndPoint = MIDIGetSource(0) 
var property : Unmanaged<CFString>? 
let err = MIDIObjectGetStringProperty(midiEndPoint, kMIDIPropertyDisplayName, &property) 
if err == noErr { 
    let displayName = property!.takeRetainedValue() as String 
    println(displayName) 
} 

Come @rintaro correttamente notato, è la takeRetainedValue() scelta giusta qui perché è il chiamanti responsabilità di rilasciare la stringa. Questo è diverso dai normali regole di gestione della memoria core della Fondazione, ma documentato nel MIDI Services Reference:

NOTA

Quando si passa un oggetto Nucleo Fondazione a una funzione MIDI, la funzione MIDI sarà mai consumare un riferimento all'oggetto. Il chiamante conserva sempre un riferimento che è responsabile del rilascio da parte di chiamando la funzione CFRelease.

Quando si riceve un oggetto Core Foundation come valore di ritorno da una funzione MIDI , il chiamante riceve sempre un nuovo riferimento all'oggetto, ed è responsabile del rilascio.

Vedere "Oggetti non gestiti" in "Working with Cocoa Data Types" per ulteriori informazioni.

UPDATE: Il codice precedente funziona solo durante la compilazione in modalità a 64 bit. Nella modalità a 32 bit, MIDIObjectRef e MIDIEndpointRef sono definiti come diversi tipi di puntatori. Questo non è un problema in (Objective-) C, ma Swift non permette una conversione diretta, "getto pericoloso" è necessario qui:

let numSrcs = MIDIGetNumberOfSources() 
println("number of MIDI sources: \(numSrcs)") 
for srcIndex in 0 ..< numSrcs { 
    #if arch(arm64) || arch(x86_64) 
    let midiEndPoint = MIDIGetSource(srcIndex) 
    #else 
    let midiEndPoint = unsafeBitCast(MIDIGetSource(srcIndex), MIDIObjectRef.self) 
    #endif 
    var property : Unmanaged<CFString>? 
    let err = MIDIObjectGetStringProperty(midiEndPoint, kMIDIPropertyDisplayName, &property) 
    if err == noErr { 
     let displayName = property!.takeRetainedValue() as String 
     println("\(srcIndex): \(displayName)") 
    } else { 
     println("\(srcIndex): error \(err)") 
    } 
} 
+0

mi ha confermato che questo funziona, ma penso che dovremmo usare 'takeRetainedValue() ', perché in questo caso, abbiamo la responsabilità di rilasciare' CFString' restituito. – rintaro

+0

@rintaro: "MIDIObjectGetStringProperty" non ha "Crea" o "Copia" nel nome. Secondo le regole di gestione della memoria del Core Foundation, ciò significa che il chiamante non è responsabile del rilascio della memoria. Vedi https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html. Penso che la "regola Get" si applica qui. –

+0

Ma, in effetti, ho confermato le perdite. vedi: https://developer.apple.com/library/mac/qa/qa1374/_index.html – rintaro

Problemi correlati