Swift CFRunLoopTimerCreate - 如何在计时器回调中获取 "self"
Swift CFRunLoopTimerCreate - how to get "self" in timer callback
如何访问 class "self" 实例以调用 class 实例方法,代码如下。如果我尝试 self.callSomeClassIntance(),如图所示,我会从编译器中得到一个 "A C function pointer cannot be formed fro a closure that captures context" 错误。我尝试 info.callSomeClassInstance(),但这会给出 "no member callSomeClassInstance" 错误。如果一行代码 xxxx.callSomeClassIntance() 被删除,代码将正确触发时间。
import Foundation
class Foo {
func callSomeClassIntance() {}
func start() {
let runLoop : CFRunLoopRef = CFRunLoopGetCurrent();
var context = CFRunLoopTimerContext(version: 0, info: unsafeBitCast(self, UnsafeMutablePointer<Void>.self), retain: nil, release: nil, copyDescription: nil)
let timer : CFRunLoopTimerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 0.1, 3.0, 0, 0, cfRunloopTimerCallback(), &context);
CFRunLoopAddTimer(runLoop, timer, kCFRunLoopCommonModes);
CFRunLoopRun()
}
func cfRunloopTimerCallback() -> CFRunLoopTimerCallBack {
return { (cfRunloopTimer, info) -> Void in
print("Fire timer...")
// need self context here to call class instance methods
self.callSomeClassIntance()
}
}
}
我们不需要捕获 self
因为我们已经在传递它了。
当您为计时器创建上下文时,您将 self
放入允许 C 代码处理它的格式,空指针:
unsafeBitCast(self, UnsafeMutablePointer<Void>.self)
此代码 returns 指向 self
的空指针。这就是您在创建上下文时为 info
参数传递的内容。
无论您在创建上下文时为 info
参数传递什么,都是用于为 CFRunLoopTimerCallback
函数的 info
参数传递的内容。因此,我们需要对 info
参数应用逆运算 (unsafeBitCast(info, Foo.self)
):
func cfRunloopTimerCallback() -> CFRunLoopTimerCallBack {
return { _, info in
let grabSelf = unsafeBitCast(info, Foo.self)
grabSelf.callSomeClassIntance()
}
}
与 How to use instance method as callback for function which takes only func or literal closure 一样,CFRunLoopTimerCallBack
必须是全局函数或不捕获上下文的闭包。
特别是,闭包不能捕获 self
因此必须
将上下文中 info
的 void 指针转换回实例
指针。
您不一定需要 cfRunloopTimerCallback()
功能,
闭包可以直接作为参数传递:
class Foo {
func callSomeClassIntance() {}
func start() {
let runLoop = CFRunLoopGetCurrent();
var context = CFRunLoopTimerContext()
context.info = UnsafeMutablePointer<Void>(Unmanaged.passUnretained(self).toOpaque())
let timer = CFRunLoopTimerCreate(kCFAllocatorDefault, 0.1, 3.0, 0, 0, {
(cfRunloopTimer, info) -> Void in
let mySelf = Unmanaged<Foo>.fromOpaque(COpaquePointer(info)).takeUnretainedValue()
mySelf.callSomeClassIntance()
}, &context);
CFRunLoopAddTimer(runLoop, timer, kCFRunLoopCommonModes);
CFRunLoopRun()
}
}
这里我使用了Unmanaged
来进行实例指针和void指针的转换。它看起来更复杂,但强调
未保留的引用 被传递。 unsafeBitCast()
如
@nhgrif 的回答可以交替使用。
你也可以定义类似于Objective-C__bridge
的函数,
比较 .
如何访问 class "self" 实例以调用 class 实例方法,代码如下。如果我尝试 self.callSomeClassIntance(),如图所示,我会从编译器中得到一个 "A C function pointer cannot be formed fro a closure that captures context" 错误。我尝试 info.callSomeClassInstance(),但这会给出 "no member callSomeClassInstance" 错误。如果一行代码 xxxx.callSomeClassIntance() 被删除,代码将正确触发时间。
import Foundation
class Foo {
func callSomeClassIntance() {}
func start() {
let runLoop : CFRunLoopRef = CFRunLoopGetCurrent();
var context = CFRunLoopTimerContext(version: 0, info: unsafeBitCast(self, UnsafeMutablePointer<Void>.self), retain: nil, release: nil, copyDescription: nil)
let timer : CFRunLoopTimerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 0.1, 3.0, 0, 0, cfRunloopTimerCallback(), &context);
CFRunLoopAddTimer(runLoop, timer, kCFRunLoopCommonModes);
CFRunLoopRun()
}
func cfRunloopTimerCallback() -> CFRunLoopTimerCallBack {
return { (cfRunloopTimer, info) -> Void in
print("Fire timer...")
// need self context here to call class instance methods
self.callSomeClassIntance()
}
}
}
我们不需要捕获 self
因为我们已经在传递它了。
当您为计时器创建上下文时,您将 self
放入允许 C 代码处理它的格式,空指针:
unsafeBitCast(self, UnsafeMutablePointer<Void>.self)
此代码 returns 指向 self
的空指针。这就是您在创建上下文时为 info
参数传递的内容。
无论您在创建上下文时为 info
参数传递什么,都是用于为 CFRunLoopTimerCallback
函数的 info
参数传递的内容。因此,我们需要对 info
参数应用逆运算 (unsafeBitCast(info, Foo.self)
):
func cfRunloopTimerCallback() -> CFRunLoopTimerCallBack {
return { _, info in
let grabSelf = unsafeBitCast(info, Foo.self)
grabSelf.callSomeClassIntance()
}
}
与 How to use instance method as callback for function which takes only func or literal closure 一样,CFRunLoopTimerCallBack
必须是全局函数或不捕获上下文的闭包。
特别是,闭包不能捕获 self
因此必须
将上下文中 info
的 void 指针转换回实例
指针。
您不一定需要 cfRunloopTimerCallback()
功能,
闭包可以直接作为参数传递:
class Foo {
func callSomeClassIntance() {}
func start() {
let runLoop = CFRunLoopGetCurrent();
var context = CFRunLoopTimerContext()
context.info = UnsafeMutablePointer<Void>(Unmanaged.passUnretained(self).toOpaque())
let timer = CFRunLoopTimerCreate(kCFAllocatorDefault, 0.1, 3.0, 0, 0, {
(cfRunloopTimer, info) -> Void in
let mySelf = Unmanaged<Foo>.fromOpaque(COpaquePointer(info)).takeUnretainedValue()
mySelf.callSomeClassIntance()
}, &context);
CFRunLoopAddTimer(runLoop, timer, kCFRunLoopCommonModes);
CFRunLoopRun()
}
}
这里我使用了Unmanaged
来进行实例指针和void指针的转换。它看起来更复杂,但强调
未保留的引用 被传递。 unsafeBitCast()
如
@nhgrif 的回答可以交替使用。
你也可以定义类似于Objective-C__bridge
的函数,
比较