http://www.cocoachina.com/ios/20150316/11335.html (通知愈多線程的關(guān)系)
http://www.reibang.com/p/2d3c8e084205 runloop相關(guān)的
http://www.reibang.com/p/a307587ac62c 通知中心
http://blog.csdn.net/wzzvictory/article/details/8489516
http://www.cnblogs.com/heyonggang/p/3681689.html 概念解析
http://www.cnblogs.com/wangyang1213/p/5234228.html
簡單的展示通知 在按鈕按下時傳值速和,
- (IBAction)buttonClick:(id)sender{
//添加字典,將label的值通過key值設(shè)置傳遞
NSDictionary *dict=[[NSDictionary alloc]initWithObjectsAndKeys:self.textFieldOne.text,@"textOne",self.textFieldTwo.text,@"textTwo",nil];
//創(chuàng)建通知
NSNotification*notification=[NSNotificationnotificationWithName:@"tongzhi"object:niluserInfo:dict];
//通過通知中心發(fā)送通知
[[NSNotificationCenterdefaultCenter] postNotification:notification];
[self.navigationControllerpopViewControllerAnimated:YES];
}
在發(fā)送通知后慢叨,在所要接收的控制器中注冊通知監(jiān)聽者懂更,將通知發(fā)送的信息接收
- (void)viewDidLoad{
[superviewDidLoad];
//注冊通知
[[NSNotificationCenterdefaultCenter]addObserver:self selector:@selector(tongzhi:) name:@"tongzhi" object:nil];
}
- (void)tongzhi:(NSNotification*)text{
NSLog(@"%@",text.userInfo[@"textOne"]);
NSLog(@"-----接收到通知------");
}
移除通知:removeObserver:和removeObserver:name:object:
其中,removeObserver:是刪除通知中心保存的調(diào)度表一個觀察者的所有入口,而removeObserver:name:object:是刪除匹配了通知中心保存的調(diào)度表中觀察者的一個入口喻旷。
這個比較簡單,直接調(diào)用該方法就行牢屋。例如:
[[NSNotificationCenter defaultCenter] removeObserver:observername:nil object:self];
注意參數(shù)notificationObserver為要刪除的觀察者掰邢,一定不能置為nil。
從使用的角度上考慮 通知中心屬于十分簡單的
我們從三個角度來理解 通知中心, 原理,多線程角度,內(nèi)存管理角度等三方面深入理解他.
1.實現(xiàn)原理,
我們可以看到 通知中心里面是兩個類 NSNotificationCenter NSNotification 而 NSNotificationCenter 是用來統(tǒng)一管理整體的通知消息
使用時 還是把 NSNotificationCenter 以單利的形式創(chuàng)建 通過 center post 通知
通過center addObserver:(id)observer selector:(SEL)Selector name:(NSString *)Name object:(id)Object 建立監(jiān)聽
,通知中心的優(yōu)點在于 一處發(fā)送,全局接受.那么也就意味著 轉(zhuǎn)發(fā)和監(jiān)聽同步的
addObserver:(id)observer selector:(SEL)Selector name:(NSString *)Name object:(id)Object 里面做了
將 name Selector Object 賦值給一個model 并且每注冊一個監(jiān)聽,就會給一個model 賦值 并且 model添加到一個可變數(shù)組中
當我們post 通知是 我們就去便利這個可變數(shù)組,并且取得數(shù)組中的model 這時候我們就可以拿到 通知的name,去執(zhí)行通知的對象 ,以及執(zhí)行的通知的方法,
[對象 performSelector:selector withObject:notification];
那么就好啦 這時候相應(yīng)的對象就去只想這個selector啦并且將參數(shù) notification傳遞過去啦
還有就是 remove 就是從數(shù)組中移除就好
一個通正中心簡單的原理就是這樣,當然還有更加會有多線程一會解釋
2.多線程的角度考慮
讀官方文檔時多次提到 通知是在那個線程發(fā)送的就在那個線程接受.
也就是會涉及到一個問題就是我實在次級線程post 然后想在其他線程接受那該怎么辦
官網(wǎng)文檔給的方法就是重定向
代碼如下:
@interface ViewController () @property (nonatomic) NSMutableArray *notifications; // 通知隊列
@property (nonatomic) NSThread *notificationThread; // 期望線程
@property (nonatomic) NSLock *notificationLock; // 用于對通知隊列加鎖的鎖對象伟阔,避免線程沖突
@property (nonatomic) NSMachPort *notificationPort; // 用于向期望線程發(fā)送信號的通信端口
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"current thread = %@", [NSThread currentThread]);
// 初始化
self.notifications = [[NSMutableArray alloc] init];
self.notificationLock = [[NSLock alloc] init];
self.notificationThread = [NSThread currentThread];
self.notificationPort = [[NSMachPort alloc] init];
self.notificationPort.delegate = self;
// 往當前線程的run loop添加端口源
// 當Mach消息到達而接收線程的run loop沒有運行時,則內(nèi)核會保存這條消息掰伸,直到下一次進入run loop
[[NSRunLoop currentRunLoop] addPort:self.notificationPort
forMode:(__bridge NSString *)kCFRunLoopCommonModes];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(processNotification:) name:@"TestNotification" object:nil];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:TEST_NOTIFICATION object:nil userInfo:nil];
});
}
- (void)handleMachMessage:(void *)msg {
[self.notificationLock lock];
while ([self.notifications count]) {
NSNotification *notification = [self.notifications objectAtIndex:0];
[self.notifications removeObjectAtIndex:0];
[self.notificationLock unlock];
[self processNotification:notification];
[self.notificationLock lock];
};
[self.notificationLock unlock];
}
- (void)processNotification:(NSNotification *)notification {
if ([NSThread currentThread] != _notificationThread) {
// Forward the notification to the correct thread.
[self.notificationLock lock];
[self.notifications addObject:notification];
[self.notificationLock unlock];
[self.notificationPort sendBeforeDate:[NSDate date]
components:nil
from:nil
reserved:0];
}
else {
// Process the notification here;
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"process notification");
}
}
@end
上面的方法是將子線程通知到主線程中去執(zhí)行任務(wù)
當然如果想在某個子線稱重執(zhí)行那么稍微修改一下就搞定啦
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.notificationThread = [NSThread currentThread];
NSLog(@"指定current thread = %@", self.notificationThread);
self.notificationPort = [[NSMachPort alloc] init];
self.notificationPort.delegate = self;
[[NSRunLoop currentRunLoop] addPort:self.notificationPort
forMode:(__bridge NSString *)kCFRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
});
創(chuàng)建子線程并且為其開啟runloop,這樣就搞定啦可以再指定的子線程中去執(zhí)行通知
通告隊列
NSNotificationQueue
對象(或者簡單稱為通告隊列)的作用是充當通告中心(NSNotificationCenter
的實例)的緩沖區(qū)皱炉。通告隊列通常以先進先出(FIFO)的順序維護通告。當一個通告上升到隊列的前面時狮鸭,隊列就將它發(fā)送給通告中心合搅,通告中心隨后將它派發(fā)給所有注冊為觀察者的對象。
每個線程都有一個缺省的通告隊列歧蕉,與任務(wù)的缺省通告中心相關(guān)聯(lián)灾部。圖5-9展示了這種關(guān)聯(lián)。您可以創(chuàng)建自己的通告隊列惯退,使得每個線程和通告中心有多個隊列赌髓。
**圖5-9 **通告隊列和通告中心
聚結(jié)的通告
NSNotificationQueue
類為Foundation Kit的通告機制增加了兩個重要的特性:即通告的聚結(jié)和異步發(fā)送。聚結(jié)是把和剛進入隊列的通告相類似的其它通告從隊列中移除的過程催跪。如果一個新的通告和已經(jīng)在隊列中的通告相類似锁蠕,則新的通告不進入隊列,而所有類似的通告(除了隊列中的第一個通告以外)都被移除懊蒸。然而荣倾,您不應(yīng)該依賴于這個特殊的聚結(jié)行為。
您可以為enqueueNotification:postingStyle:coalesceMask:forModes:
方法的第三個參數(shù)指定如下的一或多個常量骑丸,指示簡化的條件:
NSNotificationNoCoalescing
NSNotificationCoalescingOnName
NSNotificationCoalescingOnSender
您可以對NSNotificationCoalescingOnName
和NSNotificationCoalescingOnSender
常量進行位或操作舌仍,指示Cocoa同時使用通告名稱和通告對象進行聚結(jié)。那樣的話通危,和剛剛進入隊列的通告具有相同名稱和發(fā)送者對象的所有通告都會被聚結(jié)铸豁。
異步發(fā)送通告
通過NSNotificationCenter
類的postNotification:
方法及其變體,您可以將通告立即發(fā)送給通告中心黄鳍。但是推姻,這個方法的調(diào)用是同步的:即在通告發(fā)送對象可以繼續(xù)執(zhí)行其所在線程的工作之前,必須等待通告中心將通告派發(fā)給所有的觀察者并將控制權(quán)返回框沟。但是藏古,您也可以通過NSNotificationQueue
的enqueueNotification:postingStyle:
和enqueueNotification:postingStyle:coalesceMask:forModes:
方法將通告放入隊列增炭,實現(xiàn)異步發(fā)送,在把通告放入隊列之后拧晕,這些方法會立即將控制權(quán)返回給調(diào)用對象隙姿。
Cocoa根據(jù)排隊方法中指定的發(fā)送風格和運行循環(huán)模式來清空通告隊列和發(fā)送通告。模式參數(shù)指定在什么運行循環(huán)模式下清空隊列厂捞。舉例來說输玷,如果您指定NSModalPanelRunLoopMode
模式,則通告只有當運行循環(huán)處于該模式下才會被發(fā)送靡馁。如果當前運行循環(huán)不在該模式下欲鹏,通告就需要等待,直到下次運行循環(huán)進入該模式臭墨。
向通告隊列發(fā)送通告可以有三種風格:NSPostASAP
赔嚎、NSPostWhenIdle
、和NSPostNow
胧弛,這些風格將在接下來的部分中進行說明尤误。
盡快發(fā)送
以NSPostASAP
風格進入隊列的通告會在運行循環(huán)的當前迭代完成時被發(fā)送給通告中心,如果當前運行循環(huán)模式和請求的模式相匹配的話(如果請求的模式和當前模式不同结缚,則通告在進入請求的模式時被發(fā)出)损晤。由于運行循環(huán)在每個迭代過程中可能進行多個調(diào)用分支(callout),所以在當前調(diào)用分支退出及控制權(quán)返回運行循環(huán)時红竭,通告可能被分發(fā)尤勋,也可能不被分發(fā)。其它的調(diào)用分支可能先發(fā)生茵宪,比如定時器或由其它源觸發(fā)了事件斥黑,或者其它異步的通告被分發(fā)了。
您通趁汲可以將NSPostASAP
風格用于開銷昂貴的資源锌奴,比如顯示服務(wù)器。如果在運行循環(huán)的一個調(diào)用分支過程中有很多客戶代碼在窗口緩沖區(qū)中進行描畫憾股,在每次描畫之后將緩沖區(qū)的內(nèi)容刷新到顯示服務(wù)器的開銷是很昂貴的鹿蜀。在這種情況下,每個draw...
方法都會將諸如“FlushTheServer” 這樣的通告排入隊列服球,并指定按名稱和對象進行聚結(jié)茴恰,以及使用NSPostASAP
風格。結(jié)果斩熊,在運行循環(huán)的最后往枣,那些通告中只有一個被派發(fā),而窗口緩沖區(qū)也只被刷新一次。
空閑時發(fā)送
以NSPostWhenIdle
風格進入隊列的通告只在運行循環(huán)處于等待狀態(tài)時才被發(fā)出分冈。在這種狀態(tài)下圾另,運行循環(huán)的輸入通道中沒有任何事件,包括定時器和異步事件雕沉。以NSPostWhenIdle
風格進入隊列的一個典型的例子是當用戶鍵入文本集乔、而程序的其它地方需要顯示文本字節(jié)長度的時候。在用戶輸入每一個字符后都對文本輸入框的尺寸進行更新的開銷是很大的(而且不是特別有用)坡椒,特別是當用戶快速輸入的時候扰路。在這種情況下,Cocoa會在每個字符鍵入之后倔叼,將諸如“ChangeTheDisplayedSize”這樣的通告進行排隊汗唱,同時把聚結(jié)開關(guān)打開,并使用NSPostWhenIdle
風格丈攒。當用戶停止輸入的時候渡嚣,隊列中只有一個“ChangeTheDisplayedSize”通告(由于聚結(jié)的原因)會在運行循環(huán)進入等待狀態(tài)時被發(fā)出,顯示部分也因此被刷新肥印。請注意,運行循環(huán)即將退出(當所有的輸入通道都過時的時候绝葡,會發(fā)生這種情況)時并不處于等待狀態(tài)深碱,因此也不會發(fā)出通告。
立即發(fā)送
以NSPostNow
風格進入隊列的通告會在聚結(jié)之后藏畅,立即發(fā)送到通告中心敷硅。您可以在不需要異步調(diào)用行為的時候 使用NSPostNow
風格(或者通過NSNotificationCenter的postNotification:
方法來發(fā)送)。在很多編程環(huán)境下愉阎,我們不僅允許同步的行為绞蹦,而且希望使用這種行為:即您希望通告中心在通告派發(fā)之后返回,以便確定觀察者對象收到通告并進行了處理榜旦。當然幽七,當您希望通過聚結(jié)移除隊列中類似的通告時,應(yīng)該用enqueueNotification
...方法溅呢,且使用NSPostNow
風格澡屡,而不是使用postNotification:
方法。
發(fā)送通知時,這么寫 創(chuàng)建notification 然后加入隊列 并且選擇發(fā)送模式
NSNotification *notifacation = [[NSNotification alloc]initWithName:@"qqqq" object:nil userInfo:nil];
[[NSNotificationQueue defaultQueue] enqueueNotification:notifacation postingStyle:3];
合并通知
一些情況下咐旧,某一事件一旦發(fā)生驶鹉,開發(fā)者可能只想要至少發(fā)送一次通知,即使該事件可能頻繁多次發(fā)生铣墨。此時即可采用合并通知室埋,合并是把和剛進入隊列的通知相類似的其它通知從隊列中移除的過程。如果一個新的通知和已經(jīng)在隊列中的通知相類似,則新的通知不進入隊列姚淆,而所有類似的通知(除了隊列中的第一個通知以外)都被移除孕蝉。**但是,為安全起見肉盹,開發(fā)者不應(yīng)該完全依賴于這個特殊的合并行為昔驱。
我想說說 合并并沒有用過
3.從內(nèi)存管理的角度考慮
使用通知中心時,有事會報錯:thread1:exc_bad)access(code=exc_1386_gpflt)
這個錯誤的意思就是你想一個已經(jīng)釋放的對象發(fā)送了消息
那么我們回顧一下 通話中中心的使用啊
當我們 注冊一個通知中心 并且對其進行監(jiān)聽時 我們會 把自身的類 也就是self 還有通知的名字 已經(jīng)處理通知結(jié)果的的方法,放入一個model中并將model添加到一個可變數(shù)組中.當我們?nèi)ost通知時,從數(shù)組中遍歷 model 然后取出model中的值 進行
[對象 performSelector:selector withObject:notification];
然而 如果監(jiān)聽通知的類這是已經(jīng)被釋放 那么 這時再去post 那么就會想一個已經(jīng)釋放了的對象進行發(fā)送消息那么 自然而然的就會報錯.那么解決這種錯誤的方法就是
移除通知.
(1)那么為什么移除通知就不會再崩潰了呢
首先我們要理解remove的原理,前面我們知道 監(jiān)聽時會給model賦值 然后將model添加可變數(shù)組中,然后post時 便利數(shù)組取出model.根據(jù)model 去[對象 performSelector:selector withObject:notification];發(fā)送消息.
那么如果我們在可變數(shù)組中直接移除了某個已經(jīng)釋放對象的model,那么post時 就不會遍歷到相應(yīng)的model,也不會[對象 performSelector:selector withObject:notification];發(fā)送消息. 那么自然就不會發(fā)送消息崩潰
(2)如果 我們在vc1 中post 通知 在vc2中監(jiān)聽通知
我們做這樣一個通知 從vc1 跳轉(zhuǎn)到vc2 這是vc2 進行了注冊和監(jiān)聽,這是我們pop返回vc1 這是vc2 已經(jīng)釋放,這時 我們在vc1中點擊post 發(fā)送通知,你會發(fā)現(xiàn)不科學,因為沒有拋出異常.難道是理論出了錯誤. 這是我們隊 center創(chuàng)建一個分類,然后重新remove方法,(這里其實已經(jīng)覆蓋了原方法),那么在remove方法中進行打印,再重復(fù)剛才的步驟,發(fā)現(xiàn)分類執(zhí)行了remove并且打印了 同時也拋出了異常
這說明,系統(tǒng)自動幫我們執(zhí)行了 remove
好了 就到這了吧 各位道友有啥建議或者想法 歡迎評論