iOS Notification(一):註冊&發送

該文章的原創地址是:http://www.bignerdranch.com/blog/notifications-part-1-registering-and-posting/

介紹

Notification在Cocoa中是一個解藕的機制。假如你有一個對象,是一個網絡監視器,此時你想告訴其他的對象,網絡數據已經下載完畢。這個過程你可以使用很多方法來實現。

你可以下一個網絡監聽器的子類,並重載handleDisConnect方法。你也可以用target/action的方式去建立一個網絡監聽器,並關聯自己的實現方法。你也可以用責任鏈的方式,或者用delegate的方式。還可以用這篇所介紹的方式:Notification。

許多實現方式都是1對1的方式,一種更好的方法是通過notification的方式在對象之間建立間接的關係,他們之間通過廣播的方式進行通知。Notification center就像是在對象之間建立一個轉發機構,當一個通知被髮送到Notification center中,所有想要接受消息的對象就能接收到該消息。


右側的三個對象(App Delegate,data viewer和logging system)都告訴Notification center他們對網絡下載通知感興趣。當網絡下載完畢後,網絡監聽器告訴Notification center把這個消息通知給感興趣的對象。通知方和接收方之間沒有直接的關聯,他們是通過廣播的形式進行的。這種方式也被稱作發現者模式

notification center像是一箇中間人,對象可以通過它自由的像外界發送通知。這些發送通知的類不需要去繼承特殊的類,也不需要實現特殊的接口。得益於OC的runtime機制,接收通知的類可以自定義任意selector去處理這些通知,而不需要實現任何接口和callback函數。

註冊Notification

接收通知的類需要告訴notification center他們對某些感興趣。notification center就會講這些類和他們的處理函數紀錄在一個內部的列表中,就像是一個電話本。當你開始編程前,這個列表基本上就是一個空的。


然後在一個對象中寫上註冊notification的表達式,告訴notification center它對某一個通知感興趣。

[code]

// AppDelegate
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver: self
        selector: @selector(networkByeBye:)
        name: kNetworkWentByeByeNotification
        object: self.networkMonitor];

上面代碼中,AppDelegate告訴notification center,當網絡中斷的時候,它想要接受到通知,也許它可能嘗試重新連接。注意,代碼中,AppDelegate對一個名叫kNetworkWentByeByeNotification的通知感興趣,這是一個NSString類型的對象。如果networkMonitor發送一個名叫kNetworkWentByeByeNotification的通知,則AppDelegate就會用networkByeBye:去處理這個通知。Notification center會記錄下AppDelegate,networkMonitor和selector的地址,並用kNetworkWentByeByeNotification來進行標註。


此時,對象DataViewer對象想要接收網絡下載完畢的通知,然後可以改變用戶界面,它也用相似的方式註冊通知。

[code]

// DataViewer
[center addObserver: self
        selector: @selector(handleNetworkChange:)
        name: kNetworkWentByeByeNotification
        object: nil];

上面代碼中Object傳的參數是nil。這說明只要有任何對象發出kNetworkWentByeByeNotification的通知,它都能接收到,這點與AppDelegate不同,他只能接收來自networkMonitor的通知。

Notification center這個時候會在kNetworkWentByeByeNotification這個標籤下增加一條記錄,改記錄中存儲了DataViewer和selector的地址。


最後,logging system也想接受通知,這次使用使用一個較現代的API來註冊通知,其中使用到了block和operation queue的技術。

[code]

// LogOTron
// _token is an instance variable of type 'id'
_token = [center addObserverForName: kNetworkWentByeByeNotification
                 object: nil
                 queue: [NSOperationQueue mainQueue]
                 usingBlock: ^(NSNotification *notification) {
                     NSLog (@"Network went down: %@", notification);
                 }];

上述代碼是要告訴notification center,“當任何對象發送kNetworkWentByeByeNotification通知,就將block放到這個queue中去執行”,notification center會將這和對象的註冊信息像前兩次一樣進行記錄。


這個現代API與前兩種最大的不同就是,沒有對應的類和對象來處理這個notitification,前兩種註冊方式必須有一個對應的類或者對象來處理這個notification,而最後一種只需要提供一個block就就可以了,通過notification center中的記錄信息也能看到最後一種方式的特殊之處。

發送Nofication

現在已經寫好了註冊通知的信息,但是什麼效果都沒有看到。notification center只是記錄了有哪些對象對哪些通知感興趣的信息,但是還沒有對象來發送通知。

netword下載完成後,向notification center發送通知。

[code]

// NetworkMonitor
- (void) networkWentDown {
    // Collect the error
    // Pack the error and other information into a dictionary
    NSDictionary *userInfo = ...;

    // Post notification.
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center postNotificationName: kNetworkWentByeByeNotification
            object: self
            userInfo: userInfo];

} // networkWentDown

最後一行代碼,告訴notification center發送一個名爲kNetworkWentByeByeNotification的通知給感興趣的對象。notification center接收到消息之後,通知kNetworkWentByeByeNotification標籤下面的對象來接受通知,這個對象接收到通知之後,用之前註冊好的selector或者queue來處理通知。

AppDelegate接收到消息之後,會用networkByeBye來處理通知。Logging system接受到通知後,將block放進main queue中執行。

註銷通知

當一個對象對某一個通知不感興趣之後,它應該從notification center中將註冊信息註銷掉。註銷後,notification center就會刪除關於改對象的記錄信息,並斷開和改selector的弱引用,但是如果使用的是block PAI的話,它還會保留對block的引用。所以這個時候要注意是否存在循環引用。當不再接收某個通知的時候,應該儘快的註銷掉,或者在對象的dealloc時註銷通知。

註銷通知非常容易:

[code]

[center removeObserver: self];

上面的代碼是將self中的所有註冊的通知都註銷掉,爲了能比較安全的進行註銷,因爲指定註銷特定的通知,因爲有時候有些通知是深藏於Coca中的,此時如果註銷掉可能會造成系統異常。

註銷制定的通知

[code]

[center removeObserver: self
        name: kNetworkWentByeByeNotification
        object: nil];

上面的代碼只是將kNetworkWentByeByeNotification通知註銷掉的,但是其他的仍然保留。

Logging system中的通知註銷要稍微麻煩一些,因爲這個通知和改對象沒有任何關聯,用上面兩種方式是無法註銷的。所以需要在註冊block型的通知時,要將註冊信息保留下來,然後在你不需要的時候,在通過這個保留的信息將通知註銷掉,上面代碼中,是用了一個token的變量保留block API的,這個token是一個notificationcenter類型的變量。

這種block類型的API應該用下面這種方式進行註銷:

[code]

[center removeObserver: _token];

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章