其實這里的通知和之前說到的KVO功能很想,也是用於監聽操作的,但是和KVO不同的是,KVO只用來監聽屬性值的變化,這個發送監聽的操作是系統控制的,我們控制不了,我們只能控制監聽操作,類似於Android中系統發送的廣播,我們只能接受。但是通知就不一樣了,他的監聽發送也是又我們自己控制,我們可以在任何地方任何時機發送一個通知,類似於Android中開發者自己發送的廣播。從這一點看來,通知的使用場景更為廣泛了。
下面就來看一下例子:
還是護士和小孩的那個例子
Children.h
- //
- // Children.h
- // 44_KVO
- //
- // Created by jiangwei on 14-10-16.
- // Copyright (c) 2014年 jiangwei. All rights reserved.
- //
- #import <Foundation/Foundation.h>
- @interface Children : NSObject
- @property NSInteger *hapyValue;
- @end
定義了一個屬性:hapyValue
Children.m
- //
- // Children.m
- // 44_KVO
- //
- // Created by jiangwei on 14-10-16.
- // Copyright (c) 2014年 jiangwei. All rights reserved.
- //
- #import "Children.h"
- @implementation Children
- - (id) init{
- self = [super init];
- if(self != nil){
- //啟動定時器
- [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
- self.hapyValue= 100;
- }
- return self;
- }
- - (void) timerAction:(NSTimer *) timer{
- //使用set方法修改屬性值,才能觸發KVO
- _hapyValue--;
- NSLog(@"%@",_hapyValue);
- if(_hapyValue <80){
- //發送通知
- //這里和KVO的區別,我們可以手動的發送通知
- //注意通知的名稱,傳遞的參數必須和定義通知的地方的參數值要一致
- //將Children對象傳遞過去
- [[NSNotificationCenter defaultCenter] postNotificationName:@"happyValueNotification" object:self];
- }
- }
- @end
定義了一個定時器,但是我們看到這里的timerAction方法中就開始發送一個通知了:
- //發送通知
- //這里和KVO的區別,我們可以手動的發送通知
- //注意通知的名稱,傳遞的參數必須和定義通知的地方的參數值要一致
- //將Children對象傳遞過去
- [[NSNotificationCenter defaultCenter] postNotificationName:@"happyValueNotification" object:self];
我們在屬性值發生變化的地方發送一個通知:NSNotificationCenter
第一個參數:通知的名稱,這個名稱必須和后面接受通知的名稱一致
第二個參數:可以傳遞的一個參數對象
Nure.h
- //
- // Nure.h
- // 44_KVO
- //
- // Created by jiangwei on 14-10-16.
- // Copyright (c) 2014年 jiangwei. All rights reserved.
- //
- #import <Foundation/Foundation.h>
- @interface Nure : NSObject
- @end
Nure.m
- //
- // Nure.m
- // 44_KVO
- //
- // Created by jiangwei on 14-10-16.
- // Copyright (c) 2014年 jiangwei. All rights reserved.
- //
- #import "Nure.h"
- #import "Children.h"
- @implementation Nure
- - (id) init{
- self = [super init];
- if(self != nil){
- //監聽一個通知,當收到通知時,調用notificationAction方法
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationAction) name:@"happyValueNotification" object:nil];
- }
- return self;
- }
- - (void) notificationAction:(NSNotification *)notification{
- //這里我們拿到Children對象進行操作
- Children *children = notification.object;
- NSLog(@"觸發通知");
- }
- - (void)dealloc{
- //移除指定的通知,不然會造成內存泄露
- [[NSNotificationCenter defaultCenter] removeObserver:self name:@"happyValueNotification" object:nil];
- //Children對象可以添加多個通知
- //下面的方法是可以移除Children中所有通知
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- @end
在Nure類中我們開始接受通知了:
- //監聽一個通知,當收到通知時,調用notificationAction方法
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationAction) name:@"happyValueNotification" object:nil];
使用addObserver方法來監聽通知
第一個參數:監聽者對象
第二個參數:監聽處理邏輯的方法
第三個參數:通知的名稱
第四個參數:通知發送的時候傳遞過來的參數對象
1、處理通知的方法
- - (void) notificationAction:(NSNotification *)notification{
- //這里我們拿到Children對象進行操作
- Children *children = notification.object;
- NSLog(@"觸發通知");
- }
這 里會傳遞一個NSNotification對象,通過object屬性可以獲取到監聽對象了,因為我們在發送通知的時候傳遞過來的這個對象。那么這里我們 就可以獲取監聽對象的屬性值了,但是這里我們如果想知道屬性值變化前和變化后的值,我們可以在Children類中在定義一個屬性專門用來記錄舊的屬性 值,這樣就可以了。
2、銷毀方法
- - (void)dealloc{
- //移除指定的通知,不然會造成內存泄露
- [[NSNotificationCenter defaultCenter] removeObserver:self name:@"happyValueNotification" object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
在銷毀的方法中,我們可以需要移除監聽者,傳遞過去通知名
但是這里我們會注意到,還有一個方法:removeObserver方法,是用來移除所有監聽者的,因為可能有多個監聽者。
總結
OC中KVO操作和通知都是很重要的一個操作,他們的原理是基於觀察者模式的,但是KVO操作沒有通知靈活。但是KVO也有自己的優點,比如可以記錄新舊值,這個通知就比較麻煩點了,所以我們在使用的時候視情況而定,一般監聽屬性值變化的我們還是使用KVO.
使用過程中需要注意的幾點事項:
一、了解幾個相關的類
1、NSNotification
這個類可以理解為一個消息對象,其中有三個成員變量。
這個成員變量是這個消息對象的唯一標識,用於辨別消息對象。
@property (readonly, copy) NSString *name;
這個成員變量定義一個對象,可以理解為針對某一個對象的消息。
@property (readonly, retain) id object;
這個成員變量是一個字典,可以用其來進行傳值。
@property (readonly, copy) NSDictionary *userInfo;
NSNotification的初始化方法:
- (instancetype)initWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo;
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject;
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
注意:官方文檔有明確的說明,不可以使用init進行初始化
2、NSNotificationCenter
這個類是一個通知中心,使用單例設計,每個應用程序都會有一個默認的通知中心。用於調度通知的發送的接受。
添加一個觀察者,可以為它指定一個方法,名字和對象。接受到通知時,執行方法。
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;
發送通知消息的方法
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSString *)aName object:(id)anObject;
- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
移除觀察者的方法
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;
二、通知的使用流程
首先,我們在需要接收通知的地方注冊觀察者,比如:
//獲取通知中心單例對象 NSNotificationCenter * center = [NSNotificationCenter defaultCenter]; //添加當前類對象為一個觀察者,name和object設置為nil,表示接收一切通知 [center addObserver:self selector:@selector(notice:) name:@"123" object:nil];
之后,在我們需要時發送通知消息
//創建一個消息對象 NSNotification * notice = [NSNotification notificationWithName:@"123" object:nil userInfo:@{@"1":@"123"}]; //發送消息 [[NSNotificationCenter defaultCenter]postNotification:notice];
我們可以在回調的函數中取到userInfo內容,如下:
-(void)notice:(id)sender{ NSLog(@"%@",sender); }
三、多線程通知
首先看下蘋果的官方說明:
Regular notification centers deliver notifications on the thread in which the notification was posted. Distributed notification centers deliver notifications on the main thread. At times, you may require notifications to be delivered on a particular thread that is determined by you instead of the notification center. For example, if an object running in a background thread is listening for notifications from the user interface, such as a window closing, you would like to receive the notifications in the background thread instead of the main thread. In these cases, you must capture the notifications as they are delivered on the default thread and redirect them to the appropriate thread.
意思很簡單,NSNotificationCenter消息的接受線程是基於發送消息的線程的。也就是同步的,因此,有時候,你發送的消息可能不在主線程,而大家都知道操作UI必須在主線程,不然會出現不響應的情況。所以,在你收到消息通知的時候,注意選擇你要執行的線程。下面看個示例代碼
//接受消息通知的回調 - (void)test { if ([[NSThread currentThread] isMainThread]) { NSLog(@"main"); } else { NSLog(@"not main"); } dispatch_async(dispatch_get_main_queue(), ^{ //do your UI }); } //發送消息的線程 - (void)sendNotification { dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(defaultQueue, ^{ [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil]; }); }
