你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS 通知NSNotification 使用詳解

iOS 通知NSNotification 使用詳解

編輯:IOS開發綜合

如果在一個類中想要執行另一個類中的方法可以使用通知
1.創建一個通知對象:使用notificationWithName:object: 或者 notificationWithName:object:userInfo:NSNotification* notification = [NSNotification notificationWithName:kImageNotificationLoadFailed(connection.imageURL)

object:self
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:error,@"error",connection.imageURL,@"imageURL",nil]];

這裡需要注意的是,創建自己的通知並不是必須的。而是在創建自己的通知之前,采用NSNotificationCenter類的方 法postNotificationName:object: 和 postNotificationName:object:userInfo:更加便利的發出通知。這種情況,一般使用NSNotificationCenter的類方法defaultCenter就獲得默認的通知對象,這樣你就可以給該程序的默認通知中心發送通知了。注意:每一個程序都有一個自己的通知中心,即NSNotificationCenter對象。該對象采用單例設計模式,采用defaultCenter方法就可以獲得唯一的NSNotificationCenter對象。

注意:NSNotification對象是不可變的,因為一旦創建,對象是不能更改的。

2.注冊通知:addObserver:selector:name:object:

可以看到除了添加觀察者之外,還有其接收到通知之後的執行方法入口,即selector的實參。因此為了進行防御式編程,最好先檢查觀察者是否定義了該方法。例如:添加觀察者代碼有

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(aWindowBecameMain:)
name:NSWindowDidBecomeMainNotification object:nil];

這裡保證了self定義了aWindowBecameMain:方法。而對於一個任意的觀察者observer,不能保證其對應的selector有aWindowBecameMain:,可采用[observer respondsToSelector:@selector(aWindowBecameMain:)]] 進行檢查。所以完整的添加觀察者過程為:

if([observer respondsToSelector:@selector(aWindowBecameMain:)]) {
[[NSNotificationCenter defaultCenter] addObserver:observer selector:@selector(aWindowBecameMain:) name:NSWindowDidBecomeMainNotificationobject:nil];
}

注 意到addObserver:selector:name:object:不僅指定一個觀察者,指定通知中心發送給觀察者的消息,還有接收通知的名字,以 及指定的對象。一般來說不需要指定name和object,但如果僅僅指定了一個object,觀察者將收到該對象的所有通知。例如將上面的代碼中 name改為nil,那麼觀察者將接收到object對象的所有消息,但是確定不了接收這些消息的順序。如果指指定一個通知名稱,觀察者將收到它每次發出 的通知。例如,上面的代碼中object為nil,那麼客戶對象(self)將收到任何對象發出NSWindowDidBecomeMainNotification通知。如果既沒有指定指定object,也沒有指定name,那麼該觀察者將收到所有對象的所有消息。

3.發送通知:postNotificationName:object:或者performSelectorOnMainThread:withObject:waitUntilDone:

例如程序可以實現將一個文本可以進行一系列的轉換,例如對於一個實例、RTF格式轉換成ASCII格式。而轉換在一個類(如Converter類)的對象中得到處理,在誠尋執行過程中可以加入或者刪除這種轉換。而且當添加或者刪除Converter操作時,你的程序可能需要通知其他的對象,但是這些Converter對象並不需要知道被通知對象是什麼,能干什麼。你只需要聲明兩個通知,"ConverterAdded" 和 "ConverterRemoved",並且在某一事件發生時就發出這兩個通知。

當一個用戶安裝或者刪除一個Converter,它將發送下面的消息給通知中心:

[[NSNotificationCenter defaultCenter]
postNotificationName:@"ConverterAdded" object:self];

或者是

[[NSNotificationCenter defaultCenter]
postNotificationName:@"ConverterRemoved" object:self];

通知中心將會區分它們對象對這些通知感興趣並且通知他們。如果除了關心觀察者的通知名稱和觀察的對象,還關心其他之外的對象,那麼就把之外的對象放在通知的可選字典中,或者用方法postNotificationName:object:userInfo:。

而采用performSelectorOnMainThread:withObject:waitUntilDone:則是直接調用NSNotification的方法postNotification,而postNotificationName和object參數可以放到withObject的實參中。例如:

[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:notificationwaitUntilDone:YES];//注意這裡的notification為自定義的一個通知對象,可定義為NSNotification* notification = [NSNotification notificationWithName:@"ConverterAdded"object:self];//那麼它的作用與上面的一致

4.移除通知:removeObserver:和removeObserver:name:object:

其中,removeObserver:是刪除通知中心保存的調度表一個觀察者的所有入口,而removeObserver:name:object:是刪除匹配了通知中心保存的調度表中觀察者的一個入口。

這個比較簡單,直接調用該方法就行。例如:

[[NSNotificationCenter defaultCenter] removeObserver:observer name:nil object:self];

注意參數notificationObserver為要刪除的觀察者,一定不能置為nil。

PS:這裡簡單說一下通知中心保存的調度表。通知中心的調度表是給一些觀察者指定的一些通知集。一個通知集是通知中心發出的通知的子集。每個表的入口包含:

通知觀察者(必須要的)、通知名稱、通知的發送者。

下圖表示通知集中指定的通知的調用表入口的四種類型:
\

下圖表示四種觀察者的調度表

\

最後,提醒一下觀察者收到通知的順序是沒有定義的。同時通知發出和觀察的對象有可能是一樣的。通知中心同步轉發通知給觀察者,就是說postNotification: 方法直到接收並處理完通知才返回值。要想異步的發送通知,可以使用NSNotificationQueue。在多線程編程中,通知一般是在一個發出通知的那個線程中轉發,但也可能是不在同一個線程中轉發通知。

 

 


 

 

在對象間傳遞信息的標准方法是消息傳遞-即一個對象調用另一個對象的方法。然而,消息傳遞要求發送消息的對象知道消息的接收者,以及它可以響應什麼 消息。這個要求對於委托消息和其它類型的消息是可以的。有些時候,我們不希望兩個對象之間具有這種緊密的耦合-特別值得注意的原因是它會把本來獨立的子系 統聯結在一起。而且這種要求也是不切實際的,因為它需要把應用程序中很多全然不同的對象之間建立硬編碼的連接。

 

對於不能使用標准的消息傳遞的場合,Cocoa提供了通告廣播模型。通過通告機制,一個對象可以通知其它對象自己正在干什麼。在這個意義上,通告機 制類似於委托,但是它們之間的區別是很重要的。委托和通告的關鍵區別在於前者是一對一的通訊路徑(在向外委托任務的對象和被委托的對象之間)。而通告是潛 在的一對多的通訊方式-也就是一種廣播。一個對象只能有一個委托,但可以有很多觀察者,因為通告的接收者是未知的。對象不必知道那些觀察者是什麼對象。任何對象都可以間接地通過通告來觀察一個事件,並通過調整自己的外觀、行為、和狀態來響應事件。通告是一種在應用程序中進行協調和聚合的強大機制。

 

通告機制是如何工作的?這在概念上相當直接。在進程中有一個稱為通告中心的對象,充當通告的信息交換和廣播中心。在應用程序的其它地方,需要知道某 個事件的對象在通告中心進行注冊,讓它知道當該事件發生時,自己希望得到通知。這種場景的一個例子是當一個彈出式菜單被選擇時,控制器對象需要知道這個事 件,以便在用戶界面上對反映這個變化。當事件發生時,處理該事件的對象向通告中心發出一個通告,然後通告中心會將它派發給所有的相關的觀察者。圖5-8描述了這種機制。

 

請注意:通告中心同步地將通告派發給它的觀察者。發出通告的對象直到所有的通告被發出後,才重新獲得程序的控制權。如果需要以異步的方式發送通告,必須使用通告隊列(參見"通告隊列")。通告隊列在對特定的通告進行延遲處理,並根據某些具體的條件將類似的通告進行組合之後,才將通告發給通告中心。

 

任何對象都可以發出通告,也可以在通告中心注冊為通告的觀察者。發出通告的對象、通告中包含的對象、還有通告的觀察者可以是不同的對象,也可以是同 一個對象(用同一個對象作為通告的發送者和觀察者有它的用處,比如用於空閒處理方面)。發送通告的對象不需要知道通告觀察者的任何信息。另一方面,觀察者 至少需要知道通告的名稱和通告對象中封裝的字典的鍵("通告對象"部分描述了通告對象的是有什麼組成的)。

 

進一步閱讀:如果需要有關通告機制的完全討論。

 

 

本部分包含如下主要內容:

 

何時以及如何使用通告
通告對象
通告中心
通告隊列

 


 

何時以及如何使用通告

 

和委托一樣,通告機制是實現應用程序中的對象間通訊的好工具。它使應用程序中的對象可以了解其它地方發生的改變。一般地說,一個對象注冊為通告的觀 察者,是因為它希望在相應的事件發生後或即將發生時進行調整。舉例來說,如果一個定制視圖希望在窗口調整尺寸的時候改變自己的外觀,則可以觀察窗口對象發 出的NSWindowDidResizeNotification通告。通告也允許在對象間傳遞信息,因為通告中可以包含一個與事件相關的字典。

 

但是,通告和委托之間是不同的,這些差別也導致這兩種機制應該用於不同的地方。在早些時候提到,通告模型和委托模型的主要區別在於前者是廣播機制,而委托是一對一的關系。每種模型都有自己的優點,通告機制的優點如下:

 

  • 發出通告的對象不需要知道觀察者的標識。

  • 應用程序並不受限於Cocoa框架聲明的通告;任何類都可以聲明通告,其實例可以發布通告。

  • 通告並不限於應用程序內部的通訊;通過分布式通告,一個進程可以將發生的事件通知另一個進程。

 

但是,一對一的委托模型也有自己的優點。委托有機會通過將值返回給進行委托的對象來影響事件。另一方面,通告的觀察者必須發揮更為被動的作用,在響應事件時,它只能對自身及其環境產生影響。通告方法必須具有如下形式的簽名:

 

- (void)notificationHandlerName:(NSNotification *);

 

這個要求使觀察對象無法以任何直接的方式影響原來的事件。但是,委托通常可以影響進行委托的對象對事件的處理方式。而且,Application Kit對象的委托自動注冊為其通告的觀察者。您只需要實現框架類定義的通告方法,就可以接收通告。

 

在Cocoa中,通告機制並不是觀察對象狀態變化的唯一選擇,在很多情況下甚至都不是最好的選擇。Cocoa綁定技術,特別是為其提供支持的鍵-值 觀察(KVO)和鍵-值綁定(KVB)協議,也可以使應用程序中的對象觀察其它對象性質的變化。綁定機制比通告機制更為有效。在綁定機制中,被觀察對象和 觀察對象直接進行通訊,不需要像通告中心這樣的中間對象。而且,綁定機制不會像常見的通告那樣,因為處理無觀察者的變化而對性能產生不利影響。

 

但是在某些場合中,選擇通告比選擇綁定更為合理。您可能希望觀察事件,而不是對象性質的改變;或者,有些時候遵循KVO和KVB是不適合實際情況的,特別是當需要發送和觀察的通告很少的時候。

 

即使在適合使用通告的場合下,您也應該知道它對性能的潛在影響。通告發出之後,最終會通過本地的通告中心同步地派發給觀察對象。不管通告的發送是同 步的還是異步的,這個過程都是要發生的。如果有很多觀察者,或者每個觀察者在處理通告時都做很多工作,您的程序就會有明顯的延遲。因此,您應該小心,不要 過度或低效地使用通告。最後,下面這些關於通告用法的原則應該有幫助:

 

  • 對應用程序應該觀察的通告有所選擇。

  • 注冊通告時,要具體到通告的名稱和發送的對象。

  • 盡可能高效地實現處理通告的方法。

  • 避免添加或移除很多觀察者;通過一些“中間的”觀察者將通告的結果傳遞給它們可以訪問的對象要好得多。

 

  • 通告中心(NSNotificationCenter的實例),管理單任務的通告。

  • 分布式通告中心(NSDistributedNotificationCenter的實例),管理單台計算機上多任務之間的通告。

 

請注意,NSNotificationCenter和很多其它的Foundation類不同,不能和其在Core Foundation中對應的結構(CFNotificationCenterRef)進行自由橋接。

 

 

NSNotificationCenter

 

每個任務都有一個缺省的通告中心,您可以通過NSNotificationCenterdefaultCenter類方法來進行訪問。通告中心在單任務中處理通告。如果需要在同一個機器的不同任務之間進行通訊,可以使用分布式通告中心。

 

通告中心同步地將通告發送給觀察者。換句話說,在發出一個通告時,在所有的觀察者接收和處理完成通告之前,程序的控制權不會返回給發送者。如果需要異步發送通告,可以使用通告隊列,這在"通告隊列"部分中進行描述。

 

在多線程的應用程序中,通告總是在發送的線程中傳送,這個線程可能不同於觀察者注冊所在的線程。

 

 

NSDistributedNotificationCenter

 

每個任務都有一個缺省的分布式通告中心,您可以通過NSDistributedNotificationCenterdefaultCenter類方法來訪問。這個分布式通告中心負責處理同一個機器的不通任務之間的通告。如果需要實現不同機器上的任務間通訊,請使用分布式對象。

 

發送分布式通告是一個開銷昂貴的操作。通告會被發送給一個系統級別的服務器,然後再分發到注冊了該分布式通告的對象所在的任務中。發送通告和通告到達另一個任務之間的延遲是很大的。事實上,如果發出的通告太多,以致於充滿了服務器的隊列,就可能被丟棄。

 

分布式通告通過任務的運行循環來分發。任務必須運行在某種“常見”模式的運行循環下,比如NSDefaultRunLoopMode模式,才能接收分布式通告。如果接收通告的任務是多線程的,則不要以通告會到達主線程作為前提。通告通常被分發到主線程的運行循環上,但是其它線程也可以接收通告。

 

盡管常規的通告中心允許任何對象作為通告對象(也就是通告封裝的對象),分布式通告中心只支持將NSString對象作為它的通告對象。由於發出通告的對象和通告的觀察者可能位於不同的任務中,通告不能包含指向任意對象的指針。因此,分布式通告中心要求通告使用字符串作為通告對象。通告的匹配就是基於這個字符串進行的,而不是基於對象指針。

 

 

通告隊列

 

NSNotificationQueue對象(或者簡單稱為通告隊列)的作用是充當通告中心(NSNotificationCenter的實例)的緩沖區。通告隊列通常以先進先出(FIFO)的順序維護通告。當一個通告上升到隊列的前面時,隊列就將它發送給通告中心,通告中心隨後將它派發給所有注冊為觀察者的對象。

 

每個線程都有一個缺省的通告隊列,與任務的缺省通告中心相關聯。圖5-9展示了這種關聯。您可以創建自己的通告隊列,使得每個線程和通告中心有多個隊列。

 

 

聚結的通告

 

NSNotificationQueue類為Foundation Kit的通告機制增加了兩個重要的特性:即通告的聚結和異步發送。聚結是把和剛進入隊列的通告相類似的其它通告從隊列中移除的過程。如果一個新的通告和已 經在隊列中的通告相類似,則新的通告不進入隊列,而所有類似的通告(除了隊列中的第一個通告以外)都被移除。然而,您不應該依賴於這個特殊的聚結行為。

 

您可以為enqueueNotification:postingStyle:coalesceMask:forModes:方法的第三個參數指定如下的一或多個常量,指示簡化的條件:

 

  • NSNotificationNoCoalescing

  • NSNotificationCoalescingOnName

  • NSNotificationCoalescingOnSender

 

您可以對NSNotificationCoalescingOnNameNSNotificationCoalescingOnSender常量進行位或操作,指示Cocoa同時使用通告名稱和通告對象進行聚結。那樣的話,和剛剛進入隊列的通告具有相同名稱和發送者對象的所有通告都會被聚結。

 

 

異步發送通告

 

通過NSNotificationCenter類的postNotification:方法及其變體,您可以將通告立即發送給通告中心。但是,這個方法的調用是同步的:即在通告發送對象可以繼續執行其所在線程的工作之前,必須等待通告中心將通告派發給所有的觀察者並將控制權返回。但是,您也可以通過NSNotificationQueueenqueueNotification:postingStyle:enqueueNotification:postingStyle:coalesceMask:forModes:方法將通告放入隊列,實現異步發送,在把通告放入隊列之後,這些方法會立即將控制權返回給調用對象。

 

Cocoa根據排隊方法中指定的發送風格和運行循環模式來清空通告隊列和發送通告。模式參數指定在什麼運行循環模式下清空隊列。舉例來說,如果您指定NSModalPanelRunLoopMode模式,則通告只有當運行循環處於該模式下才會被發送。如果當前運行循環不在該模式下,通告就需要等待,直到下次運行循環進入該模式。

 

向通告隊列發送通告可以有三種風格:NSPostASAPNSPostWhenIdle、和NSPostNow,這些風格將在接下來的部分中進行說明。

 

vc3NpYmxl">

 

盡快發送

 

NSPostASAP風格進入隊列的通告會在運行循環的當前迭代完成時被發送給通告中心,如果當前運行循環模式和請求 的模式相匹配的話(如果請求的模式和當前模式不同,則通告在進入請求的模式時被發出)。由於運行循環在每個迭代過程中可能進行多個調用分支 (callout),所以在當前調用分支退出及控制權返回運行循環時,通告可能被分發,也可能不被分發。其它的調用分支可能先發生,比如定時器或由其它源 觸發了事件,或者其它異步的通告被分發了。

 

您通常可以將NSPostASAP風格用於開銷昂貴的資源,比如顯示服務器。如果在運行循環的一個調用分支過程中有很多客戶代碼在窗口緩沖區中進行描畫,在每次描畫之後將緩沖區的內容刷新到顯示服務器的開銷是很昂貴的。在這種情況下,每個draw...方法都會將諸如“FlushTheServer” 這樣的通告排入隊列,並指定按名稱和對象進行聚結,以及使用NSPostASAP風格。結果,在運行循環的最後,那些通告中只有一個被派發,而窗口緩沖區也只被刷新一次。

 

 

空閒時發送

 

NSPostWhenIdle風格進入隊列的通告只在運行循環處於等待狀態時才被發出。在這種狀態下,運行循環的輸入通道中沒有任何事件,包括定時器和異步事件。以NSPostWhenIdle風 格進入隊列的一個典型的例子是當用戶鍵入文本、而程序的其它地方需要顯示文本字節長度的時候。在用戶輸入每一個字符後都對文本輸入框的尺寸進行更新的開銷 是很大的(而且不是特別有用),特別是當用戶快速輸入的時候。在這種情況下,Cocoa會在每個字符鍵入之後,將諸如 “ChangeTheDisplayedSize”這樣的通告進行排隊,同時把聚結開關打開,並使用NSPostWhenIdle風 格。當用戶停止輸入的時候,隊列中只有一個“ChangeTheDisplayedSize”通告(由於聚結的原因)會在運行循環進入等待狀態時被發出, 顯示部分也因此被刷新。請注意,運行循環即將退出(當所有的輸入通道都過時的時候,會發生這種情況)時並不處於等待狀態,因此也不會發出通告。

 

 

立即發送

NSPostNow風格進入隊列的通告會在聚結之後,立即發送到通告中心。您可以在不需要異步調用行為的時候 使用NSPostNow風格(或者通過NSNotificationCenter的postNotification:方法來發送)。在很多編程環境下,我們不僅允許同步的行為,而且希望使用這種行為:即您希望通告中心在通告派發之後返回,以便確定觀察者對象收到通告並進行了處理。當然,當您希望通過聚結移除隊列中類似的通告時,應該用enqueueNotification...方法,且使用NSPostNow風格,而不是使用postNotification:方法。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved