Как использовать метод экземпляра как обратный вызов для функции, которая принимает только func или буквальное закрытие

В "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 "или буквальное закрытие"

Ответ 1

Обратный вызов является указателем на функцию 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

внутри закрытия для обновления пользовательского интерфейса при получении уведомления.

Существует решение, но это немного сложно из-за Быстрая строгая система. Точно так же, как в Swift 2 - UnsafeMutablePointer <Void> объекту, вы можете преобразовать указатель в 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)
    }

    // ...
}

См. также Как использовать self для UnsafeMutablePointer <Void> введите быстрый для получения дополнительной информации о "мостике" между указателями объектов и указателями C.