AudioToolbox、C 函数指针和 Swift
AudioToolbox, C-function pointers, and Swift
我暂时使用 AudioToolbox
API 使用 Swift 2.0 和 Xcode 7b6。 API 使用了大量的 C 语言结构,包括函数指针。这是我第一次使用 withUnsafeMutablePointer
和 unsafeBitCast
这样的命令。我正在寻找现实检查,以确保我在做的事情上没有偏离基础。
例如,要打开文件流,您可以使用以下函数:
func AudioFileStreamOpen(
_ inClientData: UnsafeMutablePointer<Void>
, _ inPropertyListenerProc: AudioFileStream_PropertyListenerProc
, _ inPacketsProc: AudioFileStream_PacketsProc
, _ inFileTypeHint: AudioFileTypeID
, _ outAudioFileStream: UnsafeMutablePointer<AudioFileStreamID>) -> OSStatus
光是函数的类型签名就让我汗流浃背。
无论如何,inClientData
参数需要是一个 UnsafeMutablePointer<Void>
,指针将指向我正在使用的 class 的实例。换句话说,它需要是指向 self
的指针。我的方法是使用 withUnsafeMutablePointer
调用函数,如下所示:
var proxy = self
let status = withUnsafeMutablePointer(&proxy) {
AudioFileStreamOpen([=12=], AudioFileStreamPropertyListener
, AudioFileStreamPacketsListener, 0, &audioFileStreamID)
}
我的第一个问题是我在这里使用的 withUnsafeMutablePointer
是否正确。我不确定如何获得指向 self
的指针 - 只写 &self
是行不通的,因为 self
是不可变的。所以我将 proxy
声明为一个变量并传递了一个对它的引用。我不知道这是否可行,但这是我想出的最好的主意。
接下来,AudioFileStreamPropertyListener
和AudioFileStreamPacketsListener
是C
回调函数。他们每个人都获得了指向 self
的指针,这是我在 AudioFileStreamOpen
中使用 withUnsafeMutablePointer
创建的。指针作为 UnsafeMutablePointer<Void>
传入,我需要将其转换回我的 class (AudioFileStream
) 的类型。为此,我相信我需要使用 unsafeBitCast
。例如,这里是 AudioFileStreamPropertyListener
:
let AudioFileStreamPropertyListener: AudioFileStream_PropertyListenerProc
= { inClientData, inAudioFileStreamID, inPropertyID, ioFlags in
let audioFileStream = unsafeBitCast(inClientData, AudioFileStream.self)
audioFileStream.didChangeProperty(inPropertyID, flags: ioFlags)
}
编译很好,但我再次不确定我是否正确使用了 unsafeBitCast
,或者在这种情况下是否使用了正确的函数。那么,unsafeBitCast
是获取 UnsafeMutablePointer<Void>
并将其转换为您可以在 C
函数指针内部实际使用的类型的正确方法吗?
有趣的是,inClientData
"context" 参数桥接为 UnsafeMutablePointer
,因为我怀疑 AudioToolbox API 会修改您的数据。好像用COpaquePointer
比较合适。可能想要 file a bug.
我认为您对 withUnsafeMutablePointer
的使用是错误的。指针 ([=18=]
) 将是 变量 proxy
的地址,而不是您的实例的地址。 (例如,您可以说 [=20=].memory = [a new instance]
来将其更改为不同的实例。这有点令人困惑,因为它的类型是 UnsafeMutablePointer<MyClass>
— 而在 Swift 中, class type 本身就是一个 pointer/reference 类型。)
我本来打算推荐你使用 Unmanaged / COpaquePointer,但我测试了它,并意识到这与 unsafeAddressOf(self)
!
完全一样
这些是等价的:
let data = UnsafeMutablePointer<Void>(Unmanaged.passUnretained(self).toOpaque())
let data = unsafeAddressOf(self)
这些是等价的:
let obj = Unmanaged<MyClass>.fromOpaque(COpaquePointer(data)).takeUnretainedValue()
let obj = unsafeBitCast(data, MyClass.self)
虽然 Unmanaged
方法在逻辑上是合理的,但我认为您可以理解为什么使用 unsafeAddressOf
/unsafeBitCast
可能更可取:-)
或者,为了您自己的方便,您可能会考虑在 Unmanaged 上进行扩展:
extension Unmanaged
{
func toVoidPointer() -> UnsafeMutablePointer<Void> {
return UnsafeMutablePointer<Void>(toOpaque())
}
static func fromVoidPointer(value: UnsafeMutablePointer<Void>) -> Unmanaged<Instance> {
return fromOpaque(COpaquePointer(value))
}
}
那么你可以使用:
let data = Unmanaged.passUnretained(self).toVoidPointer()
let obj = Unmanaged<MyClass>.fromVoidPointer(data).takeUnretainedValue()
当然,您需要确保您的对象在您期望它在回调中有效的持续时间内被保留。您可以使用 passRetained
,但我建议让您的顶级控制器抓住它。
在 https://forums.developer.apple.com/thread/5134#15725 查看一些相关讨论。
我暂时使用 AudioToolbox
API 使用 Swift 2.0 和 Xcode 7b6。 API 使用了大量的 C 语言结构,包括函数指针。这是我第一次使用 withUnsafeMutablePointer
和 unsafeBitCast
这样的命令。我正在寻找现实检查,以确保我在做的事情上没有偏离基础。
例如,要打开文件流,您可以使用以下函数:
func AudioFileStreamOpen(
_ inClientData: UnsafeMutablePointer<Void>
, _ inPropertyListenerProc: AudioFileStream_PropertyListenerProc
, _ inPacketsProc: AudioFileStream_PacketsProc
, _ inFileTypeHint: AudioFileTypeID
, _ outAudioFileStream: UnsafeMutablePointer<AudioFileStreamID>) -> OSStatus
光是函数的类型签名就让我汗流浃背。
无论如何,inClientData
参数需要是一个 UnsafeMutablePointer<Void>
,指针将指向我正在使用的 class 的实例。换句话说,它需要是指向 self
的指针。我的方法是使用 withUnsafeMutablePointer
调用函数,如下所示:
var proxy = self
let status = withUnsafeMutablePointer(&proxy) {
AudioFileStreamOpen([=12=], AudioFileStreamPropertyListener
, AudioFileStreamPacketsListener, 0, &audioFileStreamID)
}
我的第一个问题是我在这里使用的 withUnsafeMutablePointer
是否正确。我不确定如何获得指向 self
的指针 - 只写 &self
是行不通的,因为 self
是不可变的。所以我将 proxy
声明为一个变量并传递了一个对它的引用。我不知道这是否可行,但这是我想出的最好的主意。
接下来,AudioFileStreamPropertyListener
和AudioFileStreamPacketsListener
是C
回调函数。他们每个人都获得了指向 self
的指针,这是我在 AudioFileStreamOpen
中使用 withUnsafeMutablePointer
创建的。指针作为 UnsafeMutablePointer<Void>
传入,我需要将其转换回我的 class (AudioFileStream
) 的类型。为此,我相信我需要使用 unsafeBitCast
。例如,这里是 AudioFileStreamPropertyListener
:
let AudioFileStreamPropertyListener: AudioFileStream_PropertyListenerProc
= { inClientData, inAudioFileStreamID, inPropertyID, ioFlags in
let audioFileStream = unsafeBitCast(inClientData, AudioFileStream.self)
audioFileStream.didChangeProperty(inPropertyID, flags: ioFlags)
}
编译很好,但我再次不确定我是否正确使用了 unsafeBitCast
,或者在这种情况下是否使用了正确的函数。那么,unsafeBitCast
是获取 UnsafeMutablePointer<Void>
并将其转换为您可以在 C
函数指针内部实际使用的类型的正确方法吗?
有趣的是,inClientData
"context" 参数桥接为 UnsafeMutablePointer
,因为我怀疑 AudioToolbox API 会修改您的数据。好像用COpaquePointer
比较合适。可能想要 file a bug.
我认为您对 withUnsafeMutablePointer
的使用是错误的。指针 ([=18=]
) 将是 变量 proxy
的地址,而不是您的实例的地址。 (例如,您可以说 [=20=].memory = [a new instance]
来将其更改为不同的实例。这有点令人困惑,因为它的类型是 UnsafeMutablePointer<MyClass>
— 而在 Swift 中, class type 本身就是一个 pointer/reference 类型。)
我本来打算推荐你使用 Unmanaged / COpaquePointer,但我测试了它,并意识到这与 unsafeAddressOf(self)
!
这些是等价的:
let data = UnsafeMutablePointer<Void>(Unmanaged.passUnretained(self).toOpaque())
let data = unsafeAddressOf(self)
这些是等价的:
let obj = Unmanaged<MyClass>.fromOpaque(COpaquePointer(data)).takeUnretainedValue()
let obj = unsafeBitCast(data, MyClass.self)
虽然 Unmanaged
方法在逻辑上是合理的,但我认为您可以理解为什么使用 unsafeAddressOf
/unsafeBitCast
可能更可取:-)
或者,为了您自己的方便,您可能会考虑在 Unmanaged 上进行扩展:
extension Unmanaged
{
func toVoidPointer() -> UnsafeMutablePointer<Void> {
return UnsafeMutablePointer<Void>(toOpaque())
}
static func fromVoidPointer(value: UnsafeMutablePointer<Void>) -> Unmanaged<Instance> {
return fromOpaque(COpaquePointer(value))
}
}
那么你可以使用:
let data = Unmanaged.passUnretained(self).toVoidPointer()
let obj = Unmanaged<MyClass>.fromVoidPointer(data).takeUnretainedValue()
当然,您需要确保您的对象在您期望它在回调中有效的持续时间内被保留。您可以使用 passRetained
,但我建议让您的顶级控制器抓住它。
在 https://forums.developer.apple.com/thread/5134#15725 查看一些相关讨论。