Swift - 正確使用CFNotificationCenterAddObserver w /回調
問題描述
in'ViewController.swift'
創建此回調:
func callback(cf:CFNotificationCenter !, ump:UnsafeMutablePointer< Void>, cfs:CFString !, up:UnsafePointer< Void> ;, cfd:CFDictionary! - >無效{ }
使用此觀察者:
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), nil, self.callback,myMESSage, CFNotificationSuspensionBehavior.DeliverImmediately)
導致此編譯器錯誤:
AC函數指針只能通過引用一個'func'或一個文字閉包而形成
想法?
c中的CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
nil,
{(_,observer,name,_,_)print(received notification:\(name) $ b},
myMessage,
nil,
.DeliverImmediately)
$ b b
但是因為閉包不能捕獲上下文,所以沒有直接引用 self 及其屬性和實例方法。
例如,您不能添加
self.label.stringValue =got it //錯誤:不能從捕獲上下文的閉包形成C函數指針
當一個通知到達時,UI。
有一個解決方案,但由於
Swift的嚴格類型系統,它有點復雜。
類似於 Swift 2 - UnsafeMutablePointer< Void>到對象,你可以將指針轉換為self 為void指針,傳遞 observer 參數
到注冊,並將其轉換回
中的對象指針回調。
class YourClass { func callback(name:String){ print(received notification:\(name))} func registerObserver(){ //指向`self`的void指針: let observer = UnsafePointer< Void>(Unmanaged.passUnretained(self).toOpaque()) CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer, {(_,observer,name,_,_) - > Void in //提取指針從void指針到`self`: let mySelf = Unmanaged< YourClass> .fromOpaque( COpaquePointer(observer))。takeUnretainedValue() //調用實例方法: mySelf .callback(name as String)},myMessage, nil, .DeliverImmediately)} // .. }
閉包作為實例方法的trampoline / p>
指針是一個未保存的引用,因此必須確保
在釋放對象之前被移除。
更新Swift 3:
class YourClass { func callback(_ name:String){ print(received notification:\(name))} func registerObserver(){ //指向`self`的指針: let observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()) CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer, {(_,observer,name,_,_) - > 如果let observer = observer,則let name = name { // void void指針指向`self`指針: let mySelf =非托管< YourClass> .fromOpaque (觀察者).takeUnretainedValue() //調用實例方法: mySelf.callback(name.rawValue as String)} },myMessageas CFString, nil, .deliverImmediately)} // ... }
in 'ViewController.swift'
Creating this callback:
func callback(cf:CFNotificationCenter!,
ump:UnsafeMutablePointer<Void>,
cfs:CFString!,
up:UnsafePointer<Void>,
cfd:CFDictionary!) -> Void {
}
Using this observer:
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
nil,
self.callback,
"myMESSage",
nil,
CFNotificationSuspensionBehavior.DeliverImmediately)
Results in this compiler error: "A C function pointer can only be formed from a reference to a 'func' or a literal closure"
Thoughts?
The callback is a pointer to a C function, and in Swift you can pass only a global function or a closure (which does not capture any state), but not an instance method.
So this does work:
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
nil,
{ (_, observer, name, _, _) in
print("received notification: \(name)")
},
"myMessage",
nil,
.DeliverImmediately)
But since the closure cannot capture context, you have no direct reference to self and its properties and instance methods. For example, you cannot add
self.label.stringValue = "got it"
// error: a C function pointer cannot be formed from a closure that captures context
inside the closure to update the UI when a notification arrived.
There is a solution, but it is a little bit complicated due to Swift's strict type system. Similarly as in Swift 2 - UnsafeMutablePointer<Void> to object, you can convert the pointer to self to a void pointer, pass that as the observer parameter to the registration, and convert it back to an object pointer in the callback.
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)
}
// ...
}
The closure acts as a "trampoline" to the instance method.
The pointer is an unretained reference, therefore you must ensure that the observer is removed before the object is deallocated.
Update for 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)
}
// ...
}
Update for Swift 5:
發送通知
sendNotificationForMessageWithIdentifier(identifier: "broadcastStarted")
func sendNotificationForMessageWithIdentifier(identifier : String) {
let center : CFNotificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
// let userInfo : CFDictionary = CFDictionary(info)
let identifierRef : CFNotificationName = CFNotificationName(identifier as CFString)
CFNotificationCenterPostNotification(center, identifierRef, nil, nil, true)
}
///通知回調
func callback(_ name : String) {
print("received notification: \(name)")
mainView.vedioV.uploadBtn.isHidden = false
///視頻預覽
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
let userDefault = UserDefaults.init(suiteName: "group.dc.record")
let pathstr = userDefault?.object(forKey: "videoFilePath")
guard let path = pathstr else {
return
}
self?.vm.urlVedioString = URL(fileURLWithPath: path as! String)
self?.mainView.vedioV.loadImageReview(url: URL(fileURLWithPath: path as! String))
}
}
///通知注冊
func registerObserver() {
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<OTCAppealVC>.fromOpaque(observer).takeUnretainedValue()
// Call instance method:
mySelf.callback(name.rawValue as String)
}
},
"broadcastFinished" as CFString,
nil,
.deliverImmediately)
}
通知移除
deinit {
let observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())
let cfName: CFNotificationName = CFNotificationName("broadcastFinished" as CFString)
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer, cfName, nil)
}
