
圖片來源NSHipster
為什么有NSNotification
- 可以實現(xiàn)跨層的傳遞,例如A頁面導航到B頁面,B頁面再導航到C頁面,這時候如果我們通過委托回調(diào)的模式讓A知道C的一些修改,那么實現(xiàn)起來就會很麻煩。
- 可以實現(xiàn)一對多,NSNotification的實現(xiàn)是一種觀察者模式,因此可以對通知實現(xiàn)廣播。
使用通知的要點
- ** 注冊多少次,他的執(zhí)行代碼就會執(zhí)行多少次 **
//1、注冊多個通知
for (int i =0; i<3; i++) {
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(notifycationMehod:) name:@"testNotifycation" object:nil];
}
//2、傳遞通知
[[NSNotificationCenter defaultCenter]postNotificationName:@"testNotifycation" object:nil];
輸出結(jié)果
2016-12-01 17:06:23.868 NotifycationDemo[28703:10079806] receive notifycation object (null)
2016-12-01 17:06:23.868 NotifycationDemo[28703:10079806] receive notifycation object (null)
2016-12-01 17:06:23.869 NotifycationDemo[28703:10079806] receive notifycation object (null)
- 雖然注冊多次通知,但是移除一次通知,同一個對象通知就會全部移除**
//添加多個通知
for (int i =0; i<3; i++) {
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(notifycationMehod:) name:@"testNotifycation" object:nil];
}
//移除通知
[[NSNotificationCenter defaultCenter]removeObserver:self name:@"testNotifycation" object:nil];
//發(fā)送通知
[[NSNotificationCenter defaultCenter]postNotificationName:@"testNotifycation" object:nil];
這里不會有任何輸出
-
add和Remove相關(guān)方法成對出現(xiàn)
我們平時在使用通知的時候可能會在viewWillAppear方法里面add觀察者,viewWillDisappear里面remove,但是我們?nèi)绻況emove,那么如果我們導航到當前頁面再導航到另外一個頁面,然后再退回當前頁面,這時候我們會得到2個輸出結(jié)果。
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//注冊通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(notifycationMehod:) name:@"testNotifycation" object:nil];
}
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
//沒有remove通知
}
輸出結(jié)果
2016-12-01 17:24:13.722 NotifycationDemo[28820:10087367] receive notifycation object (null)
2016-12-01 17:24:13.723 NotifycationDemo[28820:10087367] receive notifycation object (null)
4 . 移除一個name沒有add的通知,不會崩潰
[[NSNotificationCenter defaultCenter]removeObserver:self name:@"unknownName" object:nil];
5 . 一個對象添加了觀察者,但是沒有移除,程序不會崩潰,這是為啥
@implementation NotifyTestClass
- (void)registerMyNotifycation{
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(notifycationMehod) name:@"testNotifycation" object:nil];
}
- (void)notifycationMehod{
NSLog(@"NotifyTestClass receive notify");
}
- (void)dealloc{
NSLog(@"NotifyTestClass dealloc");
}
@end
//局部變量 立刻釋放
NotifyTestClass *nt = [[NotifyTestClass alloc]init];
[nt registerMyNotifycation];
//發(fā)送通知
[[NSNotificationCenter defaultCenter]postNotificationName:@"testNotifycation" object:nil];
輸出結(jié)果
2016-12-01 19:29:15.039 NotifycationDemo[29035:10119206] NotifyTestClass dealloc
為了驗證這個問題我按這篇文章中提到的方法對NSNotificationCenter新建了一個category,然后重寫了remove方法;
//新建一個類別
@implementation NSNotificationCenter(NSNotificationCenterRemove)
- (void)removeObserver:(id)observer{
NSLog(@"removeObserver = %@",observer);
}
- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject{
NSLog(@"observer %@ name %@",observer,aName);
}
@end
然后我在輸出結(jié)果里面還是沒有看到在變量被dealloc之后有調(diào)用remove方法。因為按照這篇文章在最后說到通知中心對觀察者是unsafe_unretained引用,不是weak引用(weak和unsafe_unretained的區(qū)別,可以看我上一篇文章里面有提到),所以這個時候應該會崩潰的,因為相當于我對一個不存在的對象(野指針)發(fā)送了消息。這個問題困擾了我兩天沒搞明白,最后在一個群里問大家才發(fā)現(xiàn)問題的所在,蘋果官網(wǎng)文檔有說明,iOS 9.0之后NSNotificationCenter不會對一個dealloc的觀察者發(fā)送消息所以這樣就不會崩潰了。果真我換了一個8.1的手機測試,程序崩潰了,原來是做了優(yōu)化。

野指針異常

蘋果官網(wǎng)提示
6 . ViewController對象在銷毀的時候,系統(tǒng)會對他監(jiān)聽的通知進行清除
@implementation TwoViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(notifycationMehod) name:@"testNotifycation" object:nil];
// Do any additional setup after loading the view from its nib.
}
- (void)notifycationMehod{
NSLog(@"TwoViewController receive notify");
}
- (void)dealloc{
NSLog(@"TwoViewController dealloc");
}
@end
輸出結(jié)果
2016-12-02 16:36:42.175 NotifycationDemo[31359:10305477] TwoViewController dealloc
2016-12-02 16:36:42.176 NotifycationDemo[31359:10305477] removeObserver = <TwoViewController: 0x7fa42381d720>
7 . 處理方法是和post通知方法在同一線程同步執(zhí)行的
因此我們?nèi)绻邮芤恍┩ㄖ耈I的時候,我們需要回到主線程來處理。
dispatch_async(dispatch_get_main_queue(), ^{
//do your UI
});