Swift CFRunLoopTimerCreate - 如何获得" self"在计时器回调中

时间:2016-03-29 00:54:43

标签: swift cfrunloop

如何访问课程" self"实例,在给定以下代码的情况下调用类实例方法。如果我尝试self.callSomeClassIntance(),如图所示,我得到一个" A C函数指针不能形成一个捕获上下文的闭包"来自编译器的错误。我在尝试info.callSomeClassInstance(),但是这将给出一个"没有成员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()
        }

    }
}

2 个答案:

答案 0 :(得分:3)

How to use instance method as callback for function which takes only func or literal closure一样,CFRunLoopTimerCallBack 必须是一个全局函数或一个不捕获上下文的闭包。 特别是,该闭包不能捕获self因此必须 将void指针从上下文中的info转换回实例 指针。

您不一定需要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的函数, 比较How to cast self to UnsafeMutablePointer<Void> type in swift

答案 1 :(得分:2)

我们不需要捕获self,因为我们已经将其传入。

当您为计时器创建上下文时,您将self放入允许C代码处理它的格式中,这是一个无效指针:

unsafeBitCast(self, UnsafeMutablePointer<Void>.self)

此代码返回指向self的void指针。这就是您在创建上下文时为info参数传入的内容。

在创建上下文时,无论您为info参数传递了什么,都会传递info函数的CFRunLoopTimerCallback参数。因此,我们需要对unsafeBitCast(info, Foo.self)参数应用逆运算(info):

func cfRunloopTimerCallback() -> CFRunLoopTimerCallBack { 
    return { _, info in
        let grabSelf = unsafeBitCast(info, Foo.self)
        grabSelf.callSomeClassIntance()
    }
}