如何使用实例方法作为只接受func或文字闭包的函数的回调

时间:2015-10-21 13:35:37

标签: 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)

导致此编译器错误:

  

“C函数指针只能通过对'func'或文字闭包的引用形成”

2 个答案:

答案 0 :(得分:32)

回调是指向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 2 - UnsafeMutablePointer<Void> to object类似,您可以将指针转换为 self到void指针,将其作为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)
    }

    // ...
}

关闭充当了蹦床&#34;实例方法。

指针是未保留的引用,因此您必须确保 在取消分配对象之前删除观察者。

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 关于&#34;桥接&#34;在对象指针和C指针之间。

答案 1 :(得分:1)

在我的情况下,我想从闭合中调用的函数在AppDelegate中。因此,我能够使用委托从闭包中调用函数,而无需使用self。不管这是一个想法,还是有更多经验的人都必须对此发表评论。

        self.pingSocket = CFSocketCreate(kCFAllocatorDefault, AF_INET, SOCK_DGRAM, IPPROTO_ICMP,CFSocketCallBackType.dataCallBack.rawValue, {socket, type, address, data, info in
            //type is CFSocketCallBackType
            guard let socket = socket, let address = address, let data = data, let info = info else { return }

// stuff deleted, including use of C pointers

            let appDelegate = NSApplication.shared.delegate as! AppDelegate
            appDelegate.receivedPing(ip: sourceIP, sequence: sequence, id: id)
            //}
            return
        }, &context)

extension AppDelegate: ReceivedPingDelegate {
    func receivedPing(ip: UInt32, sequence: UInt16, id: UInt16) {
        // stuff deleted
    }
}
protocol ReceivedPingDelegate: class {
    func receivedPing(ip: UInt32, sequence: UInt16, id: UInt16)
}