NSNotification是iOS中壹個調度消息通知的類,采用單例設計模式,在開發中實現傳值、回調等。在iOS中,NSNotification是使用觀察者模式來實現用於跨層傳遞消息。
NSNotification包含了壹些用於向其他對象發送通知的必要信息,包括名稱、對象和可選字典,並由NSNotificationCenter或NSDistributedNotificationCenter的實例進行發送。name是標識通知的標記、object是保存發送通知的對象、userinfo存儲其他相關對象。 這裏主要註意的是:NSNotification對象是不可變的。
可以使用 notificationWithName:object: 或 notificationWithName:object:userInfo: 創建通知對象。但實際開發中,壹般是直接使用NSNotificationCenter調用 postNotificationName:object: 或 postNotificationName:object:userInfo: ,這兩個類方法會在內部直接創建NSNotification對象,並發出通知。
?從官網文檔可知,NSNotification是不能直接實例化的,如果用init方法進行實例化時,會引發異常。還有需要註意的是如果我們自己去實現構造方法時,不能在super上調用init方法。
NSNotificationCenter提供了壹套機制來發送通知,每個運行中的應用程序都有壹個defaultCenter通知中心,我們可以創建新的通知中心來組織特定上下文中的通信。?NSNotificationCenter暴露給外部的字段只有壹個defaultCenter,並且該字段是只讀的,暴露出來的方法分為三種:添加、移除通知觀察者和發出通知。詳細如下表所示:
postNotificationName:object:
postNotificationName:object:userInfo: |
相關說明:
簡單理解為:通知中心的緩沖區。盡管通知中心已經分發通知,但放置到隊列中的通知可能會延遲,直到runloop結束或者runloop空閑時才發送。如果有多個相同的通知,NSNotificationQueue會將其進行合並,以便在發布多個通知的情況下只發送壹個通知。
?通知隊列按照先進先出(FIFO)的順序維護通知。當壹個通知移動到隊列的前面時,隊列將它發送到通知中心,然後再將通知分派給所有註冊為觀察者的對象。每個線程都有壹個默認的通知隊列,該隊列與流程的默認通知中心相關聯。我們也可以創建自己的通知隊列。
?和NSNotificationCenter壹樣,NSNotificationQueue也只暴露了壹個字段:defaultQueue,返回當前線程的默認通知隊列。方法分為:創建通知隊列和管理通知。詳細說明如下表所示:
dequeueNotificationsMatching:coalesceMask:
enqueueNotification:postingStyle:coalesceMask:forModes: |
方法相關說明:
在上面的方法中,需要註意的2個常量,相關說明如下:
NSNotificationCenter定義了兩個Table,同時為了封裝觀察者信息,也定義了Observation保存觀察者信息。他們的結構體可以簡化如下所示:
在NSNotificationCenter內部壹***保存了兩張表,壹張用於保存添加觀察者的時候傳入的NotificationName的情況;壹張用於保存添加觀察者的時候沒有傳入NotificationCenter的情況,詳細分析如下:
在Named Table中,NotificationName作為表的key,因為我們在註冊觀察者的時候是可以傳入壹個object參數用於只監聽該對象發出的通知,並且壹個通知可以添加多個觀察者,所以還需要壹張表用來保存object和observe的對應關系。這張表的key、value分別是以object為key,observe為value。所以對於Named Table,最終的結構為:
Named Table
特別說明:在實際開發中,我們經常將object參數傳nil,這個時候系統會根據nil自動產生壹個key。相當於這個key對應的value(鏈表)保存的就是對於當前NotificationName沒有傳入object的所有觀察者。當NotificationName被發送時,在鏈表中的觀察者都會收到通知。
UNamed Table結構比Named Table簡單得多。因為沒有NotificationName作為key。這裏直接就以object為key,比Named Table少了壹層Table嵌套。
UnNamed Table
如果在註冊觀察者時沒有傳入NotificationName,同時沒有傳入object,所有的系統通知都會發送到註冊的對象裏。
首先在初始化NSNotificationCenter時會創建壹個對象,這個對象裏面保存了Named Table、UNamed Table和其他信息。
在沒有傳入NotificationName的情況和上面的過程類似,只不過是直接根據object去對應的鏈表而已。如果既沒有傳入NotificationName,也沒有傳入object,則這個觀察者會添加到wildcard鏈表中。
發送通知壹般是調用 postNotificationName:object:userInfo: 方法來實現。該方法內部會實例化壹個NSNotification來保存傳入的各種參數,包括name、object和userinfo。
?發送通知的流程總體來說就是根據NotificationName查找到對應的Observer鏈表,然後遍歷整個鏈表,給每個Observer結點中保存的對象及SEL,來向對象發送消息。具體流程如下:
這個方式也就能說明,發送通知的線程和接收通知的線程都是同壹個線程。
NSNotification和線程同步之間是什麽關系呢?先看下官方文檔的說明:
翻譯過來意思為:
更多關於NSNotification與線程之間的關系,請閱讀下面的文章: iOS開發 - NSNotification和線程相關
總的來說,NSNotification的三個相關類的作用,可以用下圖進行歸納總結。
總結