在 Swift 中,為了避免 Notification 名稱直接使用字符串不安全,引入了 NSNotification.Name 類型,rawValue 為 String。雖然這樣安全性有了一定的保證,但用起來(lái)就不是那么順手了。
必須先把字符串用 NSNotification.Name 類型封裝起來(lái),再傳給 NSNotificationCenter:
class A {
static let notificationName = Notification.Name("aNotificationName")
NotificationCenter.default.post(name: notificationName, object: nil)
}
不過,這么寫安全性并沒有得到保障,因?yàn)?NSNotificationCenter 是全局的,如果有其它類也使用 aNotificationName
字符串創(chuàng)建通知名稱,就會(huì)造成通知錯(cuò)亂。
稍微優(yōu)化一下,給 NSNotification.Name 寫一個(gè)擴(kuò)展,用靜態(tài)常量來(lái)表示通知名稱:
extension Notification.Name {
static let aNotification = NSNotification.Name("aNotification")
static let bNotification = NSNotification.Name("bbNotification")
}
這樣用起來(lái)就方便些:
NotificationCenter.default.addObserver(, selector: , name: .aNotification, object: nil)
NotificationCenter.default.post(name: .aNotification, object: nil)
不過這么處理還會(huì)有些問題,聲明跟系統(tǒng)通知方式相同,使用編譯器提示會(huì)跟系統(tǒng)的通知混在一起不好找。還有就是,因?yàn)橛^察者模式降低了對(duì)象之間的偶爾,也就導(dǎo)致寫下面這樣的代碼,你都不知道想監(jiān)聽的是誰(shuí),做了什么。
NotificationCenter.default.addObserver(self, selector:#selector(notificationReceived) , name: .aNotification, object: nil)
這些信息就需要程序員自己在方法名或者通知名中流露,如果是別人命名的可能就會(huì)看不懂。即使順著通知名跳過去,還得再去找是哪個(gè)類發(fā)出來(lái)的。
使用 Swift protocol 可以解決這些的困擾。
先上代碼:
protocol Notifier {
associatedtype Notification: RawRepresentable
}
extension Notifier where Notification.RawValue == String {
fileprivate static func name(for notification: Notification) -> String {
return "\(self).\(notification.rawValue)"
}
func postNotification(_ notification: Notification, object: Any? = nil, userInfo: [String : Any]? = nil) {
Self.postNotification(notification, object: object, userInfo: userInfo)
}
static func postNotification(_ notification: Notification, object: Any? = nil, userInfo: [String : Any]? = nil) {
let aName = name(for: notification)
NotificationCenter.default
.post(name: Foundation.Notification.Name(rawValue: aName), object: object, userInfo: userInfo)
}
static func addObserver(_ observer: Any, selector: Selector, notification: Notification, object: Any? = nil) {
let aName = name(for: notification)
NotificationCenter.default
.addObserver(observer, selector: selector, name: NSNotification.Name(rawValue: aName), object: object)
}
static func removeObserver(_ observer: Any, notification: Notification, object: Any? = nil) {
let aName = name(for: notification)
NotificationCenter.default
.removeObserver(observer, name: NSNotification.Name(rawValue: aName), object: object)
}
}
實(shí)現(xiàn)起來(lái)并不難,只是用協(xié)議擴(kuò)展對(duì) NotificationCenter 的接口做了一層封裝,需要發(fā)通知的類關(guān)聯(lián)一個(gè) RawRepresentable
類型就實(shí)現(xiàn)了這個(gè)協(xié)議。
因?yàn)?enum 默認(rèn)遵循 RawRepresentable
協(xié)議 ,這里配合枚舉來(lái)定義通知的類型簡(jiǎn)直完美。
struct Cat {
private func feelHungry() {
self.postNotification(.hungry)
}
}
extension Cat: Notifier {
enum Notification: String {
case hungry
case sleepy
}
}
Cat 向外發(fā)通知:
self.postNotification(.hungry)
其它類注冊(cè)并處理通知:
Cat.addObserver(self, selector: #selector(feedCat), notification: .hungry)
@objc
func feedCat() { }
這樣從 addObserver
就可以清晰的看出,觀察的對(duì)象以及對(duì)應(yīng)的行為,而且通知名還很簡(jiǎn)潔。
到這里 NSNotificationCenter 的使用姿勢(shì)已經(jīng)介紹完畢。
再補(bǔ)充探討兩點(diǎn) NSNotificationCenter 相關(guān)的內(nèi)容:
1、
在 iOS 9 之前,observer 如果沒有調(diào)用 removeObserver
,有時(shí)候會(huì)出現(xiàn)野指針的奔潰,是因?yàn)?NSNotificationCenter 不會(huì)持有 observer,對(duì) observer 的引用是 unowned
的。如果 observer 已經(jīng)銷毀,卻沒有從通知中心移除,通知中心還是會(huì)給它發(fā)消息,如果這塊地址被覆蓋的話就出現(xiàn)無(wú)法響應(yīng)消息的奔潰。
在 iOS 9 之后,可以不用調(diào)用 removeObserver
了,NSNotificationCenter 以 weak
的方式引用 observer,釋放后指針會(huì)自動(dòng)置為 nil
。
2、
Notification 中含有一個(gè) object
參數(shù),一般使用的時(shí)候都會(huì)傳個(gè) nil
。開始接觸的時(shí)候以為只是用于廣播傳值的,其實(shí)并僅僅是這樣。
public init(name: Notification.Name, object: Any? = default, userInfo: [AnyHashable : Any]? = default)
我們知道 name
是用來(lái)標(biāo)記通知類型的,object
可以理解為通知的二級(jí)標(biāo)記,當(dāng) name
相同時(shí),通知的分發(fā)就交給 object
來(lái)判斷了。
如果addObserver
時(shí) object
為 nil
,就會(huì)忽略 object
用 name
來(lái)判斷觀察者是誰(shuí)。如果 name
為 nil
,會(huì)根據(jù) object
來(lái)判斷觀察者是誰(shuí)。
那如果這么寫會(huì)怎么樣?
NotificationCenter.default.addObserver(self, selector: #selector(received), name: nil, object: nil)
name
和 object
都為 nil
, NotificationCenter 會(huì)忽略通知類型的匹配,只要有通知就向給這個(gè) observer,received
就會(huì)收到所有類型的通知,當(dāng)然也包括系統(tǒng)的。
Swift中Notification.Name這么難用怎么辦
Let’s Build NSNotificationCenter
Swift: NotificationCenter Protocol
如果喜歡本文,可以收藏或者關(guān)注,第一時(shí)間獲得更新推送。