swift - 如何将实例方法用作仅采用 func 或文字闭包的函数的回调

标签 swift callback

在“ViewController.swift”中我正在创建这个回调:

func callback(cf:CFNotificationCenter!, 
    ump:UnsafeMutablePointer<Void>, 
    cfs:CFString!, 
    up:UnsafePointer<Void>, 
    cfd:CFDictionary!) -> Void {

}

使用这个观察者:

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), 
    nil, 
    self.callback, 
    "myMESSage", 
    nil, 
    CFNotificationSuspensionBehavior.DeliverImmediately)

此编译器错误的结果:

"A C function pointer can only be formed from a reference to a 'func' or a literal closure"

最佳答案

回调是一个指向 C 函数的指针,在 Swift 中你可以通过 只有一个全局函数或一个闭包(不捕获任何状态), 但不是实例方法。

所以这确实有效:

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
        nil,
        { (_, observer, name, _, _) in
            print("received notification: \(name)")
        },
        "myMessage",
        nil,
        .DeliverImmediately)

但由于闭包无法捕获上下文,您无法直接引用 self 及其属性和实例方法。 例如,您不能添加

self.label.stringValue = "got it"
// error: a C function pointer cannot be formed from a closure that captures context

在通知到达时在闭包内更新 UI。

有一个解决方案,但由于以下原因有点复杂 Swift 的严格类型系统。 类似于 Swift 2 - UnsafeMutablePointer<Void> to object ,您可以将指针转换为 self 为空指针,将其作为 observer 参数传递 注册,并将其转换回一个对象指针 回调。

class YourClass { 

    func callback(name : String) {
        print("received notification: \(name)")
    }

    func registerObserver() {

        // Void pointer to `self`:
        let observer = UnsafePointer<Void>(Unmanaged.passUnretained(self).toOpaque())

        CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
            observer,
            { (_, observer, name, _, _) -> Void in

                // Extract pointer to `self` from void pointer:
                let mySelf = Unmanaged<YourClass>.fromOpaque(
                        COpaquePointer(observer)).takeUnretainedValue()
                // Call instance method:
                mySelf.callback(name as String)
            },
            "myMessage",
            nil,
            .DeliverImmediately)
    }

    // ...
}

闭包充当实例方法的“蹦床”。

指针是一个未保留的引用,因此你必须确保 在对象被释放之前移除观察者。


Swift 3 更新:

class YourClass {

    func callback(_ name : String) {
        print("received notification: \(name)")
    }

    func registerObserver() {

        // Void pointer to `self`:
        let observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())

        CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
            observer,
            { (_, observer, name, _, _) -> Void in
                if let observer = observer, let name = name {

                    // Extract pointer to `self` from void pointer:
                    let mySelf = Unmanaged<YourClass>.fromOpaque(observer).takeUnretainedValue()
                    // Call instance method:
                    mySelf.callback(name.rawValue as String)
                }
            },
            "myMessage" as CFString,
            nil,
            .deliverImmediately)
    }

    // ...
}

另见 How to cast self to UnsafeMutablePointer<Void> type in swift了解更多信息 关于对象指针和 C 指针之间的“桥接”。

关于swift - 如何将实例方法用作仅采用 func 或文字闭包的函数的回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33260808/

相关文章:

ios - Swift 数据缓存 - 最佳实践

javascript - 超时 2000ms 超过 mocha

c++11 - fbthrift (facebook thrift) 稳定版发布了吗?

ios - iOS 中如何避免回调 hell ?

ios - Swift Ios 设置委托(delegate)而不将 self 作为其他 View Controller

json - 我的登录身份验证有问题

swift - 如何为我的场景添加背景? SpriteKit swift 4

ios - 从另一个 View Controller 更新 UIPageControl

java - 设计模式: Callback as a method parameter

c++ - 传递回调成员函数的高效方法