3

在给定以下代码的情况下,如何访问类“self”实例以调用类实例方法。如果我尝试 self.callSomeClassIntance(),如图所示,我会从编译器收到“无法从捕获上下文的闭包形成 AC 函数指针”错误。我尝试 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()
        }

    }
}
4

2 回答 2

3

如何使用实例方法作为仅采用 func 或文字闭包的函数的回调一样,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了实例指针和空指针之间的转换。它看起来更复杂,但强调 未保留的引用被传递。unsafeBitCast()如@nhgrif's answer可以替代使用。

您还可以定义类似于 Objective-C 的函数__bridge,比较How to cast self to UnsafeMutablePointer<Void> type in swift

于 2016-03-29T17:06:29.663 回答
2

我们不需要捕获self,因为我们已经在传递它。

当你为你的定时器创建上下文时,你将self放入一个允许 C 代码处理它的格式,一个 void 指针:

unsafeBitCast(self, UnsafeMutablePointer<Void>.self)

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

创建上下文时为参数传递的info任何内容都是用于传递函数info参数的内容CFRunLoopTimerCallback。因此,我们需要将逆运算 ( unsafeBitCast(info, Foo.self)) 应用于该info参数:

func cfRunloopTimerCallback() -> CFRunLoopTimerCallBack { 
    return { _, info in
        let grabSelf = unsafeBitCast(info, Foo.self)
        grabSelf.callSomeClassIntance()
    }
}
于 2016-03-29T01:19:18.700 回答