NSNotification,又叫通知,屬于設(shè)計(jì)模式中的觀察者模式,在開(kāi)發(fā)中很常見(jiàn),相信大家都不陌生。關(guān)于NSNotification的用法以及它與delegate的區(qū)別,都是些老生常談的話題了,這里就不再說(shuō)了。這篇文章主要想說(shuō)幾個(gè)平時(shí)開(kāi)發(fā)過(guò)程中,大家可能都沒(méi)有注意到的細(xì)節(jié),我相信,當(dāng)你理解了這些細(xì)節(jié),你對(duì)NSNotification的認(rèn)識(shí)會(huì)上升到另一個(gè)高度。
1、NSNotificationCenter是一種“同步”機(jī)制
先看看官方文檔的一段說(shuō)明(Xcode->Window->Documentation And API Reference,搜索NSNotification):
A notification center delivers notifications to observers synchronously. In other words, when posting a notification, control does not return to the poster until all observers have received and processed the notification.
意思是,當(dāng)notification center post了一個(gè)notification后,只有當(dāng)所有observer接收了notification并且處理完了后程序才會(huì)返回,才會(huì)繼續(xù)執(zhí)行post下面的代碼。
我們寫(xiě)段代碼來(lái)驗(yàn)證一下,也幫助加深理解。
- 注冊(cè)通知
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:kNotificationName object:nil];
}
- 處理通知
注意這里將線程休眠了5秒
- (void)handleNotification:(NSNotification *)notification {
NSString *msg = [NSString stringWithFormat:@"receive notification:%@", notification.object];
NSLog(@"%@", msg);
//休眠5s
sleep(5);
NSLog(@"notification handle finish...");
}
- 發(fā)送通知
- (IBAction)pushOnMainThread:(id)sender {
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationName object:@"pushOnMainThread"];
NSLog(@"return...");
}
看下執(zhí)行結(jié)果

可以看到,當(dāng)我們postNotification后,Observer接收到notification并處理,確實(shí)是只有當(dāng)Observer處理完成后(休眠了5s)程序才執(zhí)行了postNotification后的代碼(打印return...),而且這期間主屏幕會(huì)卡主(因?yàn)橥綀?zhí)行,要等待程序返回)
由此可見(jiàn):** 接收到通知后不易做耗時(shí)操作,否則可能會(huì)卡住主線程,導(dǎo)致屏幕卡頓。**
另外,提一下,可以通過(guò)Notification Queues實(shí)現(xiàn)異步發(fā)送通知(暫時(shí)沒(méi)去研究...??)
2、NSNotificationCenter可以在多線程里執(zhí)行
還是先看一下官方文檔的說(shuō)明:
In a multithreaded application, notifications are always delivered in the thread in which the notification was posted, which may not be the same thread in which an observer registered itself.
意思是,** notification在哪個(gè)線程里post,Observer就會(huì)在哪個(gè)線程里接收并處理,這個(gè)線程可能不是最初Observer被添加的線程。 **
在上面那段代碼里,我們是在主線程上添加的Observer(viewDidLoad里)
我們現(xiàn)在通過(guò)子線程post一個(gè)notification看看結(jié)果:
- handleNotification里打印當(dāng)前線程
- (void)handleNotification:(NSNotification *)notification {
NSString *msg = [NSString stringWithFormat:@"receive notification:%@[%@]", notification.object, [NSThread currentThread]];
NSLog(@"%@", msg);
}
- 子線程中postNotification
- (IBAction)pushOnOtherThread:(id)sender {
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"post notification on other thread..%@",[NSThread currentThread]);
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationName object:@"pushOnOtherThread"];
});
}
執(zhí)行結(jié)果如下:

可以看到,正如官方文檔里說(shuō)明的那樣,當(dāng)我們?cè)谄渌€程里postNotification時(shí),Observer確實(shí)是在那個(gè)線程里執(zhí)行的。
所以,這里就會(huì)有個(gè)坑,當(dāng)你要在Observer里更新UI的時(shí)候,如果notification是在其他線程里post的,那就可能會(huì)出現(xiàn)一些奇怪的問(wèn)題了,比如主線程不響應(yīng),甚至是crash(注意crash不是必然的)。
- handleNotification里更新UI,異步postNotification
- (void)handleNotification:(NSNotification *)notification {
NSString *msg = [NSString stringWithFormat:@"receive notification:%@[%@]", notification.object, [NSThread currentThread]];
self.msgLabel.text = msg;
NSLog(@"%@", msg);
}
-
執(zhí)行結(jié)果
程序沒(méi)有crash,msgLabel上的text確實(shí)正確顯示出來(lái)了,不過(guò)是延遲了幾秒鐘后才顯示出來(lái)的。同時(shí)在控制臺(tái)里會(huì)出現(xiàn)下面這段內(nèi)容:
可以看到,接收到notification后又過(guò)了6秒,系統(tǒng)給出了一段說(shuō)明,告知這個(gè)操作可能會(huì)導(dǎo)致奇怪的crash,但不是必定會(huì)crash,但是這種操作應(yīng)該被避免。
結(jié)束語(yǔ)
說(shuō)了一大堆,其實(shí)對(duì)平時(shí)開(kāi)發(fā)可能幫助不大,因?yàn)榇蠹乙呀?jīng)習(xí)慣了在主線程里addObserver、postNotification,可能完全不會(huì)在意它可不可以在多線程里運(yùn)行。Who care!
But! 如果你知道了這些,可能會(huì)對(duì)以后面試有幫助哦~~~??????
