开发者问题收集

快速函数指针

2016-12-23
3709

我正在关注这个 教程 ,特别是我在用 Swift 语言转换这个函数时遇到了问题:

- (id)init
{
    CFRunLoopSourceContext    context = {0, self, NULL, NULL, NULL, NULL, NULL,
                                    &RunLoopSourceScheduleRoutine,
                                    RunLoopSourceCancelRoutine,
                                    RunLoopSourcePerformRoutine};

    runLoopSource = CFRunLoopSourceCreate(NULL, 0, &context);
    commands = [[NSMutableArray alloc] init];

    return self;
}

其中,在 init 函数中 context 变量给我带来了问题。

在上面的代码中,context 是一个类型为: CFRunLoopSourceContext 的变量,而苹果文档中这个对象的初始化是 像这个

因此,我在初始化时使用了以下代码,专注于 schedule 参数:

var context = CFRunLoopSourceContext(version: 0, info: bridge(obj: self) ,
                                         retain: nil,
                                         release: nil,
                                         copyDescription: nil,
                                         equal: nil,
                                         hash: nil,
                                         schedule: RunLoopSourceScheduleRoutine,
                                         cancel: nil,
                                         perform: nil)

函数 RunLoopSourceScheduleRoutine 如下所示:

    func RunLoopSourceScheduleRoutine(info:UnsafeMutableRawPointer? ,rl:CFRunLoop? , mode:CFRunLoopMode?)  {
    
    let obj :  RunLoopSource = Unmanaged<RunLoopSource>.fromOpaque(info!).takeUnretainedValue()
    let theContext = RunLoopContext(withSource: obj, andLoop: rl!)
    performSelector(onMainThread: #selector(myMethod), with: theContext, waitUntilDone: false)
    
}

但编译器给出了以下错误消息: c 函数指针只能由对“func”或文字闭包的引用形成

即使我创建了以下闭包:

let runLoopSourceScheduleRoutine = { (info:UnsafeMutableRawPointer? ,rl:CFRunLoop? , mode:CFRunLoopMode?)-> Void in return
        
        let obj :  RunLoopSource = Unmanaged<RunLoopSource>.fromOpaque(info!).takeUnretainedValue()
        let theContext = RunLoopContext(withSource: obj, andLoop: rl!)
        performSelector(onMainThread: #selector(myMethod), with: theContext, waitUntilDone: false)
        
    }

我这样写:

var context = CFRunLoopSourceContext(version: 0, info: bridge(obj: self) ,
                                         retain: nil,
                                         release: nil,
                                         copyDescription: nil,
                                         equal: nil,
                                         hash: nil,
                                         schedule: runLoopSourceScheduleRoutine,
                                         cancel: nil,
                                         perform: nil)

也给出了相同的错误。 问题是什么? 有什么提示吗

1个回答

如果您使用 func RunLoopSourceScheduleRoutine() 作为回调,那么它必须是 全局函数 ,而不是实例方法。

如果您将回调定义为闭包,那么它需要标记为纯 C 回调:

let runLoopSourceScheduleRoutine: @convention(c) (UnsafeMutableRawPointer?, CFRunLoop?, CFRunLoopMode?) -> Void =
    { (info, rl, mode) in

        let obj = Unmanaged<RunLoopSource>.fromOpaque(info!).takeUnretainedValue()
        // ...
}

或者,将闭包表达式传递给编译器,编译器会将类型推断为 C 回调:

var context = CFRunLoopSourceContext(version: 0, info: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) ,
                 retain: nil,
                 release: nil,
                 copyDescription: nil,
                 equal: nil,
                 hash: nil,
                 schedule: { (info, rl , mode) in

                    let obj = Unmanaged<RunLoopSource>.fromOpaque(info!).takeUnretainedValue()
                    // ...
                 }, cancel: nil,
                 perform: nil)

还请注意,您必须在 obj 上调用该方法,而不是在 self 上调用该方法。 我建议使用 GCD 而不是 performSelector() ,这允许 编译器检查该方法是否使用正确的参数调用:

let obj = Unmanaged<RunLoopSource>.fromOpaque(info!).takeUnretainedValue()
let theContext = RunLoopContext(withSource: obj, andLoop: rl!)
DispatchQueue.main.async {
    obj.myMethod(theContext)
}
Martin R
2016-12-23