首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Swift中构造UnsafeMutablePointer<Unmanaged<CFData>>类型?

如何在Swift中构造UnsafeMutablePointer<Unmanaged<CFData>>类型?
EN

Stack Overflow用户
提问于 2019-04-27 09:23:35
回答 2查看 547关注 0票数 1

我正在使用Swift与Core进行交互,MIDIThruConnectionFind函数也有一些问题。

文档声明如下

代码语言:javascript
复制
func MIDIThruConnectionFind(_ inPersistentOwnerID: CFString, 
                      _ outConnectionList: UnsafeMutablePointer<Unmanaged<CFData>>) -> OSStatus

这是我的功能,无论我尝试什么,我都会得到构建错误。例如,变量被使用,但没有初始化,类型错误等等。

代码语言:javascript
复制
@IBAction func listConnections(_ sender: Any) {
    var connectionRef: Unmanaged<CFData>

    MIDIThruConnectionFind("" as CFString, &connectionRef)        
}

我所期望的是,我应该为outConnectionList提供一个指针的入口,并且该函数为数据分配内存。但我该怎么用斯威夫特呢?

更新

至少这是编译的,但是如何去引用和访问数据呢?

代码语言:javascript
复制
@IBAction func listConnections(_ sender: Any) {
    let connectionRefs = UnsafeMutablePointer<Unmanaged<CFData>>.allocate(capacity: 1)

    MIDIThruConnectionFind("" as CFString, connectionRefs)
}
EN

回答 2

Stack Overflow用户

发布于 2019-04-27 11:00:18

我有点猜测,目前还不能实际测试代码,但以下是我的想法:

MIDIThruConnectionFind()函数在MIDIThruConnection.h中声明为

代码语言:javascript
复制
extern OSStatus
MIDIThruConnectionFind(     CFStringRef                     inPersistentOwnerID,
                            CFDataRef __nonnull * __nonnull outConnectionList )

因此进口到Swift

代码语言:javascript
复制
public func MIDIThruConnectionFind(_ inPersistentOwnerID: CFString,
            _ outConnectionList: UnsafeMutablePointer<Unmanaged<CFData>>) -> OSStatus

这意味着最后一个参数必须是(初始化和)非可选Unmanaged<CFData>值的地址。

但是这是没有意义的:数据是由函数分配的,我们不想传递任何数据。我强烈地假设这是C头中该函数的空注释中的一个bug。其他带有out参数的核心MIDI函数被正确地注释。

代码语言:javascript
复制
extern OSStatus
MIDIObjectGetStringProperty(    MIDIObjectRef           obj,
                                CFStringRef             propertyID,
                                CFStringRef __nullable * __nonnull str )        

以下解决方法可能有效:将connectionRef声明为可选指针(以便将其初始化为nil),并在调用函数时将其“强制转换”到非可选指针:

代码语言:javascript
复制
var connectionRef: Unmanaged<CFData>?

let status = withUnsafeMutablePointer(to: &connectionRef) {
    $0.withMemoryRebound(to: Unmanaged<CFData>.self, capacity: 1) {
        MIDIThruConnectionFind("" as CFString, $0)
    }
}

如果成功,则可以打开可选指针,并使用CFData获得takeRetainedValue()引用。CFData是连接到NSData的免费桥接,可以转换为Swift覆盖类型的Data

代码语言:javascript
复制
if status == noErr, let connectionRef = connectionRef {
    let data = connectionRef.takeRetainedValue() as Data

}

另一个解决方法是在桥接头文件中定义带有正确的空注释的包装器函数:

代码语言:javascript
复制
#include <CoreMIDI/CoreMIDI.h>

static OSStatus myMIDIThruConnectionFind(CFStringRef inPersistentOwnerID,
                                          CFDataRef __nullable * __nonnull outConnectionList) {
    return MIDIThruConnectionFind(inPersistentOwnerID, outConnectionList);
}

它可以被称为

代码语言:javascript
复制
var connectionRef: Unmanaged<CFData>?
let status = myMIDIThruConnectionFind("" as CFString, &connectionRef)
票数 1
EN

Stack Overflow用户

发布于 2020-12-29 11:48:24

  • 要快速回答,你就像这样
代码语言:javascript
复制
// Creates an unmanaged<CFData>>
var unmanagedData = Unmanaged.passUnretained(Data() as CFData)

是否使用保留或未保留取决于上下文。对于coreMidi对象,我假设CoreMidi负责内存管理。

  • 而关于“如何访问数据”,你需要反弹记忆。

下面是两个与CoreMidi交互的函数的例子。一个用于单个值,另一个用于值数组(如在您的问题中)。

注意,在现实生活中,最好将函数声明为抛出,并在CoreMidi函数返回错误时抛出一些快速错误。在这个例子中,如果失败,我们只返回0。

单值情况

代码语言:javascript
复制
    func getMidiThruParams(connectionRef: MIDIThruConnectionRef) -> MIDIThruConnectionParams? {
        // 1 - allocate an unmanaged CFData
        var unmanagedData = Unmanaged.passUnretained(Data() as CFData)

        // 2 - Pass the data pointer to C API
        let err = MIDIThruConnectionGetParams(connectionRef, &unmanagedData)
        guard err == noErr else {
            return nil
        }

        // 3 - Extract the data from unmanaged CFData
        let data = unmanagedData.takeUnretainedValue() as Data

        // 4 - Remap to the swift type
        return data.withUnsafeBytes { bytes -> MIDIThruConnectionParams in
            UnsafeRawPointer(bytes).assumingMemoryBound(to: MIDIThruConnectionParams.self).pointee
        }
    }

阵列案例

代码语言:javascript
复制
    func getMidiThruConnections() -> [MIDIThruConnectionRef]? {
        
        // 1 - allocate an unmanaged data reference
        var unmanagedData = Unmanaged.passUnretained(Data() as CFData)
        
        // 2 - Pass the data pointer to C API
        let err = MIDIThruConnectionFind("com.moosefactory.midiCenter.midiThru" as CFString, &unmanagedData)
        guard err == noErr else {
            return nil
        }
        
        // 3 - Extract the CFData from unmanaged data
        // We prefer CFData to Data here, to access pointer and size
        let cfData = unmanagedData.takeUnretainedValue()
        guard let dataPtr = CFDataGetBytePtr(cfData) else {
            return nil
        }
        
        // 4  - Compute the number of elements
        let dataSize = CFDataGetLength(cfData)
        let numberOfConnections = dataSize / MemoryLayout<MIDIThruConnectionRef>.stride
        
        // 5 - Rebound pointer from <Int8> to <MIDIThruConnectionRef>
        return dataPtr.withMemoryRebound(to: MIDIThruConnectionRef.self,
                                         capacity: numberOfConnections) { typedPtr in
            // Convert pointer to buffer pointer
            let bufferPointer = UnsafeBufferPointer(start: typedPtr, count: numberOfConnections)
            // Construct array
            return [MIDIThruConnectionRef].init(unsafeUninitializedCapacity: numberOfConnections) { refPtr, count in
                count = numberOfConnections
                for i in 0..<count {
                    refPtr[i] = bufferPointer[i]
                }
            }
        }
    }
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55879035

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档