【问题标题】:Swift UnsafeMutablePointer<Unmanaged<CFString>?> allocation and printSwift UnsafeMutablePointer<Unmanaged<CFString>?> 分配和打印
【发布时间】:2015-01-26 00:00:24
【问题描述】:

我是 swift 新手,在处理非托管 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,而且 swift 的情况有很大不同。

编辑:

在 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

我真的什么都不懂……

更新:

终于Martin找到了解决办法,看来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 位设备...

感谢您的宝贵帮助

【问题讨论】:

    标签: ios swift unmanaged midi unsafe-pointers


    【解决方案1】:

    我没有使用 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:

    注意

    当将 Core Foundation 对象传递给 MIDI 函数时,MIDI 函数永远不会消耗对对象的引用。呼叫者,召集者 始终保留一个由它负责释放的引用 调用 CFRelease 函数。

    当从 MIDI 接收 Core Foundation 对象作为返回值时 函数,调用者总是收到对该对象的新引用, 并负责发布。

    有关详细信息,请参阅"Working with Cocoa Data Types" 中的“非托管对象”。

    更新:以上代码仅在 64 位模式下编译时有效。在 32 位模式下, MIDIObjectRefMIDIEndpointRef 被定义为不同类型的指针。 这在 (Objective-)C 中没有问题,但是 Swift 不允许直接转换, 这里需要“不安全的演员表”:

    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)")
        }
    }
    

    【讨论】:

    • 我确认这可行,但我认为我们应该使用takeRetainedValue(),因为在这种情况下,我们有责任释放返回的CFString
    • @rintaro:“MIDIObjectGetStringProperty”的名称中没有“Create”或“Copy”。根据 Core Foundation 内存管理规则,这意味着调用者不负责释放内存。见developer.apple.com/library/mac/documentation/CoreFoundation/…。我认为“获取规则”适用于此。
    • 但是,事实上,我确认它泄漏了。见:developer.apple.com/library/mac/qa/qa1374/_index.html
    • 抱歉有点坚持,但我不知道是否有人看到了我的其他消息,我仍然无法使代码正常工作。我不知道在stackoverflow上添加评论或新“答案”之间的最佳实践是什么。非常感谢
    • @Martin 成功了!这完全疯狂,我现在不明白原因。这是平常的事吗?它是否来自其他东西的 iOS SDK 版本?无论如何,我可以对你说一声“谢谢”,因为我自己肯定不会找到这个。
    猜你喜欢
    • 2016-03-09
    • 1970-01-01
    • 1970-01-01
    • 2015-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-31
    • 2018-05-28
    相关资源
    最近更新 更多