Swift UnsafeMutablePointer <Unmanaged <CFString>?> Distribution and Printing
I'm new to fast, and I have some problems with unmanaged CFString (or NSString) pointers. Am I working on a CoreMIDI project that involves using UnsafeMutablePointer? >, As you can see in this function:
func MIDIObjectGetStringProperty(_ obj: MIDIObjectRef, _ propertyID: CFString!, _ str: UnsafeMutablePointer<Unmanaged<CFString>?>) -> OSStatus
My problem is that I want to allocate a buffer to receive the contents of the property (_str), then call the function above and finally print the contents in the console using println.
At the moment, I wrote this:
// 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)
I did not find sample code for using CoreMIDI functions in the Apple Developers Library, and not on the Internet. I am really confused because I come from cpp and everything changes very quickly.
EDIT:
After the answers of Rintaro and Martin, I still have a problem, all my tests run on iOS 8.1, and if I copy the code that you brought me, the compiler tells me that I can not write:
let err = MIDIObjectGetStringProperty(midiEndPoint, kMIDIPropertyDisplayName, &property)
Unmanaged results are not converted to MIDIObjectRef. So I added "&" because MIDIObjectRef is UnsafeMutablePointer <void>.
let midiEndPoint = MIDIGetSource(0) var property : Unmanaged<CFString>? let err = MIDIObjectGetStringProperty(&midiEndPoint, kMIDIPropertyDisplayName, &property)
Now: "Unmanaged <MIDIEndpoint>" does not convert to '@lvalue inout $ T2'. Finally, I had to change the first option to var, not understanding why?!?
var midiEndPoint = MIDIGetSource(0) var property : Unmanaged<CFString>? let err = MIDIObjectGetStringProperty(&midiEndPoint, kMIDIPropertyDisplayName, &property)
Now the code compiles and runs, but MIDIObjectGetStringProperty returns OSStatus err -50, which matches IOW or from MacErros.h:
paramErr = -50, /*error in user parameter list*/
Thus, it seems that the parameters are not what MIDIObjectGetStringProperty expects.
Source "0" exists on my iPad because MIDIGetNumberOfSources () returns 1. Here is the full code:
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
I do not understand anything...
UPDATE:
Finally, Martin found a solution, it seems that there are two different definitions of MIDIObjectRef in 32 and 64-bit architectures. When I run the code on the old iPad 2, my code tried to compile in 32 bit mode, where the return value of MIDIGetSource (i) is not converted to MIDIObjectRef. The solution is to "unsafe cast" the midi endpoint on 32-bit architectures:
#if arch(arm64) || arch(x86_64) let midiEndPoint = MIDIGetDestination(i) #else let midiEndPoint = unsafeBitCast(MIDIGetDestination(i), MIDIObjectRef.self) #endif
... Or buy a new 64-bit device ...
Thanks for the precious help.
I have no experience with CoreMIDI and I canβt test it, but this is how it should work:
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) }
As @rintaro correctly pointed out, takeRetainedValue()
is the right choice here because the responsibility for releasing this line lies with the callers. This differs from the usual Core Foundation memory management rules, but is documented in the MIDI Services Reference :
Note
When passing a Core Foundation object to a MIDI function, the MIDI function will never use the reference to the object. The caller always saves the link for which he is responsible for releasing the CFRelease function.
When a Core Foundation object is received as a return value from a MIDI function, the caller always receives a new reference to the object and is responsible for freeing it.
See "Unmanaged Objects" in Working with Cocoa Data Types for details .
UPDATE: The above code only works when compiled in 64-bit mode. In 32-bit mode, MIDIObjectRef
and MIDIEndpointRef
defined as different types of pointers. This is not a problem in (Objective-) C, but Swift does not allow direct conversion, "Insecure casting" is needed here:
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)") } }