1、通知的移除
(1)針對(duì)ViewController來(lái)說(shuō):不管是iOS9之前,還是iOS9之后,系統(tǒng)在調(diào)用dealloc方法時(shí),會(huì)自動(dòng)調(diào)用[[NSNotificationCenter defaultCenter]removeObserver:self],所以使用addObserver添加監(jiān)聽(tīng)者的話無(wú)需手動(dòng)移除通知。
(2)針對(duì)NSObject來(lái)說(shuō),iOS9之前系統(tǒng)在調(diào)用dealloc方法時(shí),不會(huì)自動(dòng)調(diào)用[[NSNotificationCenter defaultCenter]removeObserver:self],所以需要手動(dòng)移除,不然會(huì)崩潰。iOS9之后,增加了在調(diào)用dealloc方法時(shí),自動(dòng)調(diào)用[[NSNotificationCenter defaultCenter]removeObserver:self]的方法,所以添加通知后,無(wú)需手動(dòng)移除通知。
注意:在viewWillAppear方法里面注冊(cè)通知,需要在viewWillDisappear方法里移除通知,不然頁(yè)面多次調(diào)用viewWillAppear會(huì)導(dǎo)致通知多次回調(diào)
1. 為什么 iOS9之前需要手動(dòng)移除通知了呢?
在iOS9之前,觀察者注冊(cè)時(shí),通知中心并不會(huì)對(duì)觀察者對(duì)象做 retain操作,而是對(duì)觀察者對(duì)象進(jìn)行unsafe_unretained(不安全) 引用。導(dǎo)致觀察者對(duì)象被釋放后,不安全引并不會(huì)自動(dòng)被置為nil,從而變成野指針,對(duì)野指針發(fā)送消息導(dǎo)致程序崩潰。
2. 什么是unsafe_unretained引用?
不安全引用(unsafe reference)和弱引用 (weak reference) 類似,它并不會(huì)讓被引用的對(duì)象保持存活,但是和弱引用不同的是,當(dāng)被引用的對(duì)象釋放的時(shí),不安全引用并不會(huì)自動(dòng)被置為 nil,這就意味著它變成了野指針,而對(duì)野指針發(fā)送消息會(huì)導(dǎo)致程序崩潰。
3. 為什么要進(jìn)行unsafe_unretained引用?
因?yàn)?Cocoa 和 Cocoa Touch 中的一些類仍然還沒(méi)有支持 weak 引用。所以,當(dāng)我們想對(duì)這些類使用 weak 引用的時(shí)候,只能用unsafe_unretained來(lái)替代。
4. 為什么 iOS9之后不需要手動(dòng)移除通知了呢?
從 iOS 9 開(kāi)始通知中心會(huì)對(duì)觀察者進(jìn)行 weak 引用,所以不需要在觀察者對(duì)象釋放之前從通知中心移除
但是使用- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block 這個(gè)方法添加的通知,仍需手動(dòng)移除通知,因?yàn)橥ㄖ行膶?duì)它的持有是強(qiáng)引用
添加通知
_observe = [[NSNotificationCenter defaultCenter]addObserverForName:@"noti3" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"%s",__func__);
}];
參數(shù)queue:接收到通知的回調(diào)在哪個(gè)線程中調(diào)用,如果傳mainQueue則通知在主線程回調(diào),否則在子線程回調(diào)
移除通知
[[NSNotificationCenter defaultCenter] removeObserver:_observe];
這個(gè)block里面,對(duì)self進(jìn)行了強(qiáng)引用,所以在block使用self時(shí),要進(jìn)行弱引用,避免循環(huán)引用引發(fā)的內(nèi)存泄漏。
2、通知的同步異步
1、使用(void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject方式添加通知,
(1)默認(rèn)通知在哪個(gè)線程發(fā)出,就在哪個(gè)線程接收
(2)默認(rèn)通知的發(fā)送和接收和同步的
poster發(fā)出一個(gè)通知后,通知事件之后的代碼被堵塞,通知中心會(huì)一直等待所有的observer(監(jiān)聽(tīng)者)都收到,并且處理完通知才會(huì)返回到poster
當(dāng)然為了防止阻塞,可以將通知改成異步
方式1: 將
observer收到通知后的方法,放在子線程中執(zhí)行,這種方式接收到通知還是同步,但是接收到通知后的處理改成異步
- (void)receiveNoti2:(NSNotification *)noti{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"%@",noti.object);
NSLog(@"%@",[NSThread currentThread]);
});
}
方式2、通過(guò)
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle;和- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle coalesceMask:(NSNotificationCoalescing)coalesceMask forModes:(nullable NSArray<NSRunLoopMode> *)modes發(fā)送通知,不是讓通知回調(diào)在異步執(zhí)行,只是讓通知等待到runloop空閑的時(shí)候再去執(zhí)行
發(fā)送通知方式如下
NSNotification *noti = [NSNotification notificationWithName:@"noti2" object:nil];
[[NSNotificationQueue defaultQueue] enqueueNotification:noti postingStyle:NSPostWhenIdle coalesceMask:NSNotificationNoCoalescing forModes:@[NSDefaultRunLoopMode]];
NSLog(@"sss=========%@",[NSThread currentThread]);
接收到通知后
- (void)receiveNoti2:(NSNotification *)noti{
NSLog(@"%s",__func__);
NSLog(@"%@",[NSThread currentThread]);
sleep(3);
NSLog(@"nnnnnn ===== %@",[NSThread currentThread]);
}

由圖可見(jiàn),發(fā)送通知后,先完成poster后面的事情,再完成通知的發(fā)送和observer接收到通知后的處理,整個(gè)流程都是在主線程中進(jìn)行,并沒(méi)有切換線程,只是讓通知等待到runloop空閑的時(shí)候再去執(zhí)行
NSPostingStyle
- NSPostWhenIdle:runloop空閑的時(shí)候回調(diào)通知方法(runloop 進(jìn)入kCFRunLoopBeforeWaiting)
- NSPostASAP:runloop能夠調(diào)用的時(shí)候就回調(diào)通知方法(runloop 進(jìn)入kCFRunLoopBeforeTimers)
- NSPostNow:runloop立即回調(diào)通知方法(和默認(rèn)通知方式一樣)
詳情查看iOS 通知的多線程處理 & 與Runloop的關(guān)系
coalesceMask
- NSNotificationNoCoalescing = 0, // 不合成
- NSNotificationCoalescingOnName = 1, // 根據(jù)NSNotification的name字段進(jìn)行合成
- NSNotificationCoalescingOnSender = 2 // 根據(jù)NSNotification的object字段進(jìn)行合成
2、使用- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block這個(gè)方法添加的通知,通知回調(diào)的block在哪個(gè)線程執(zhí)行只和queue參數(shù)有關(guān),無(wú)論發(fā)送通知是在主線程或子線程發(fā)送的,都不會(huì)影響。