我有我从 Swift 使用的 C API。
在斯威夫特,我有:
enum GetSnapshotResult {
case success(snapshot: UIImage, String)
case failure()
}
func getSnapshot(completion: @escaping (GetSnapshotResult) -> Void) {
CAPIGetSnapshot(nil) { (_) in
completion(
.success(
snapshot: UIImage(),
"test"
)
)
}
}
Run Code Online (Sandbox Code Playgroud)
在 C API 中:
void CAPIGetSnapshot(void * ptr, void(*callbackOnFinish)(void *)) {
//do something in background thread
//and on its finish, call callbackOnFinish from thread
callbackOnFinish(ptr);
}
Run Code Online (Sandbox Code Playgroud)
但是,有了这个,我得到:
AC 函数指针不能由捕获上下文的闭包形成
我该如何解决这个问题?
您需要一个包装类,以便可以通过 C 函数将指向该实例的空指针传送到回调。的组合passRetained(),并takeRetainedValue()确保包装实例完成功能后解除被调用。
func getSnapshot(completion: @escaping (GetSnapshotResult) -> Void) {
class Wrapper {
let completion: (GetSnapshotResult) -> Void
init(completion: @escaping (GetSnapshotResult) -> Void) {
self.completion = completion
}
}
let wrapper = Wrapper(completion: completion)
let observer = UnsafeMutableRawPointer(Unmanaged.passRetained(wrapper).toOpaque())
CAPIGetSnapshot(observer) { theObserver in
let theWrapper = Unmanaged<Wrapper>.fromOpaque(theObserver!).takeRetainedValue()
theWrapper.completion(
.success( snapshot: UIImage(), "test")
)
}
}
Run Code Online (Sandbox Code Playgroud)
一些备注:
我假设 C 函数将ptr参数传递给回调。
passRetained(wrapper)保留对象。这确保在getSnapshot()函数返回时不会释放包装器实例。
takeRetainedValue()在闭包中消耗了保留。因此,当闭包返回时,包装器实例被释放。
completion是一个闭包,闭包是引用类型。wrapper.completion只要包装器实例存在,就持有对该闭包的引用。
当然,您可以在闭包中使用相同的变量名(“observer”、“wrapper”)。我在这里选择了不同的名称(“theObserver”、“theWrapper”)只是为了强调它们是不同的变量,即闭包不再捕获上下文。
observer需要是一个可变的原始指针只是因为 C 函数的第一个参数被声明为void * ptr. 如果您可以将函数声明更改为
void CAPIGetSnapshot(const void * ptr, void(*callbackOnFinish)(const void *))
Run Code Online (Sandbox Code Playgroud)
然后也能let observer = UnsafeRawPointer(...)工作。
有关对象引用与 void 指针之间转换的更多信息,请参见例如如何在 swift 中将 self 强制转换为 UnsafeMutablePointer<Void> 类型。
除了自定义包装类之外,您还可以利用这样一个事实,即任意 Swift 值在强制转换为时会自动装箱到类类型中AnyObject(参见例如AnyObject not working in Xcode8 beta6?)。
func getSnapshot(completion: @escaping (GetSnapshotResult) -> Void) {
let wrapper = completion as AnyObject
let observer = UnsafeRawPointer(Unmanaged.passRetained(wrapper).toOpaque())
CAPIGetSnapshot(observer) { theObserver in
let theCompletion = Unmanaged<AnyObject>.fromOpaque(theObserver!).takeRetainedValue()
as! ((GetSnapshotResult) -> Void)
theCompletion(
.success( snapshot: UIImage(), "test")
)
}
}
Run Code Online (Sandbox Code Playgroud)
强制解包和强制转换在这里是安全的,因为您知道它传递给函数的内容。解包或转换失败将指示编程错误。但我更喜欢第一个版本,而不是依赖这种“魔法”。
| 归档时间: |
|
| 查看次数: |
232 次 |
| 最近记录: |