如您所见,使用int * 效率相当低。那是因为对该值所做的分配不能触发任何其他代码运行(在 C 中没有与 didSet 观察者等效的代码)。所以你不得不创建一个单独的线程,其目的是定期轮询 int 值,这非常浪费。
要解决此问题,您需要使用回调。这些在 Swift 中很容易,因为闭包是一等公民,您可以轻松地创建和传递它们。在 C 中,没有这样的东西。最接近的是函数指针,但它们是静态的,并且缺少闭包可以提供的上下文存储。这意味着您不能将 Swift 实例方法或闭包作为 c 函数指针传递。您需要一个标记为@convention(c) 的闭包,它禁止它捕获任何状态(包括方法中的self)。
但是有一个解决方法。要理解它,首先你应该了解 C 中使用的典型回调模式。
在 C 中,接受函数指针(作为回调)的函数通常还有第二个无类型 (void *)“上下文”参数。这个想法是你可以手动传入你自己的上下文,无论是什么类型,它都会与函数指针一起存储。当触发回调时,C 函数将调用你的函数指针,并传入你给它的上下文。在您的回调函数(您将其指针作为回调传递的那个)中,您可以访问此void *context,然后您可以将其转换为您的适当类型并访问您需要的任何内容。
此上下文参数有许多名称,例如context、ctx、userdata、userinfo(以及下划线、驼峰式变体)等。
如果您将 C 函数更新为:
typedef void (*ProgressChangedCallback)(const int newProgress, void *userinfo);
void doSomething(ProgressChangedCallback callback, void *userinfo) {
// when the progress updates:
callback(newProgress, userinfo);
}
在 Swift 中,您可以利用这个 userinfo 参数来传递实现回调所需的上下文。有两种流行的方法:
- 您可以使用
userinfo 参数偷运self,然后调用self 上的方法,并完全访问对象的实例变量。
- 您可以使用
userinfo 参数在适当的Swift 闭包中走私。闭包本身可以捕获状态,包括 self 或您可能需要的任何其他内容。
看起来像这样:
// ProgressChangedCallback would have type `@convention(c) (Int, UnsafeMutableRawPointer) -> Void`
class MyClass {
var someInstanceState = 0
func someFunctionThatHasAccessToInstanceState() {
defer { someInstanceState += 1 }
print(someInstanceState)
// 5. This can update your progress bar UI or whatever.
}
func registerCallbackForDoSomething() {
let myProgressChangedCallback: ProgressChangedCallback = { newProgress, userInfo in
// 3. Unpack `userinfo` to get access to our instance again
let instance = Unmanaged<MyClass>.fromOpaque(userInfo).takeUnretainedValue()
// 4. Do whatever we might need with the instance
instance.someFunctionThatHasAccessToInstanceState()
}
// 1. Retain `self`, and get an opaque pointer to it
let retainedPointerToSelf = UnsafeMutableRawPointer(Unmanaged.passRetained(self).toOpaque())
// 2. Register our callback, and pass the pointer to self as `userInfo`
doSomething(myProgressChangedCallback, retainedPointerToSelf)
}
}
进一步阅读