我是新手,并且在处理非托管CFString(或NSString)的指针时遇到一些困难。我正在一个CoreMIDI项目上工作,这暗示着使用UnsafeMutablePointer?>,如您在此函数中看到的:
func MIDIObjectGetStringProperty(_ obj: MIDIObjectRef, _ propertyID: CFString!, _ str: UnsafeMutablePointer<Unmanaged<CFString>?>) -> OSStatus
我的问题是我想分配一个缓冲区以接收属性(_str)的内容,然后调用上面的函数,最后使用println在控制台中打印内容。
此刻我写了这个:
// 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)
我没有在苹果开发者库中找到任何示例代码来使用CoreMIDI函数,而不是在互联网上。我真的很困惑,因为我来自cpp,事情很快就大不相同了。
编辑:
在Rintaro和Martin回答我仍然有问题之后,我所有的测试都在iOS 8.1上完成,如果我复制了您带给我的代码,则编译器告诉我我无法编写:
let err = MIDIObjectGetStringProperty(midiEndPoint, kMIDIPropertyDisplayName, &property)
“非托管”中的结果不能转换为“ MIDIObjectRef”。因此,我添加了“&”,因为MIDIObjectRef是UnsafeMutablePointer 。
let midiEndPoint = MIDIGetSource(0) var property : Unmanaged<CFString>? let err = MIDIObjectGetStringProperty(&midiEndPoint, kMIDIPropertyDisplayName, &property)
现在:’Unmanaged ‘不能转换为’@lvalue inout $ T2’。最后,我不得不将第一个let更改为var,而不了解为什么?!?
var midiEndPoint = MIDIGetSource(0) var property : Unmanaged<CFString>? let err = MIDIObjectGetStringProperty(&midiEndPoint, kMIDIPropertyDisplayName, &property)
代码现在可以编译并运行,但是MIDIObjectGetStringProperty返回OSStatus err -50,它对应于IOW或来自MacErros.h:
paramErr = -50, /*error in user parameter list*/
因此,似乎该参数不是MIDIObjectGetStringProperty正在等待的参数。
iPad上确实存在源“ 0”,因为MIDIGetNumberOfSources()返回1。这是完整的代码:
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)) } }
显示:
MIDI Destinations : 1 error : -50
我真的什么都不懂…
更新:
最终,马丁找到了解决方案,似乎在32位和64位体系结构中存在MIDIObjectRef的两种不同定义。当我在旧的iPad 2上运行代码时,我的代码试图以32位模式进行编译,其中MIDIGetSource(i)的返回值无法转换为MIDIObjectRef。解决方案是“不安全地”转换32位体系结构上的midi端点:
#if arch(arm64) || arch(x86_64) let midiEndPoint = MIDIGetDestination(i) #else let midiEndPoint = unsafeBitCast(MIDIGetDestination(i), MIDIObjectRef.self) #endif
…或购买新的64位设备…
谢谢您的宝贵帮助
我没有使用CoreMIDI的经验,也无法对其进行测试,但是这应该是这样的:
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) }
正如@rintaro正确注意到的那样,takeRetainedValue()此处是正确的选择,因为释放字符串是 调用者的 责任。这与通常的Core Foundation内存管理规则不同,但在 MIDI Services Reference中有记录:
takeRetainedValue()
注意 将Core Foundation对象传递给MIDI函数时,MIDI函数将永远不会消耗对该对象的引用。调用方始终保留一个引用,它负责通过调用CFRelease函数来释放引用。 从MIDI函数接收Core Foundation对象作为返回值时,调用者始终会收到对该对象的新引用,并负责释放它。
注意
将Core Foundation对象传递给MIDI函数时,MIDI函数将永远不会消耗对该对象的引用。调用方始终保留一个引用,它负责通过调用CFRelease函数来释放引用。
从MIDI函数接收Core Foundation对象作为返回值时,调用者始终会收到对该对象的新引用,并负责释放它。
有关更多信息,请参见“ 使用可可数据类型”中的 “非托管对象” 。
更新: 上面的代码仅在以64位模式进行编译时有效。在32位模式下, MIDIObjectRef和MIDIEndpointRef被定义为不同类型的指针。在(Objective-)C中这没问题,但是Swift不允许直接转换,这里需要“不安全的转换”:
MIDIObjectRef
MIDIEndpointRef
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)") } }