-
通知的概念
一些基本的概念就不做介紹了,應該都明白,好了,直接上代碼
為了方便查看,發送通知和接受通知就放在同一個文件里,一般項目不會這么用,但是操作都是一樣的
可以po一下 [[NSNotificationCenter defaultCenter],你會發現其實就是個列表,包含了系統通知,用戶通知等
- (void)viewDidLoad {
[super viewDidLoad];
//object:指定接受某個對象的通知,為nil表示可以接受任意對象的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotifi:) name:@"EdisonNotif" object:nil];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self sendNotification];
}
-(void)sendNotification{
NSLog(@"發送通知before:%@",[NSThread currentThread]);
[[NSNotificationCenter defaultCenter] postNotificationName:@"EdisonNotif" object:nil];
NSLog(@"發送通知after:%@",[NSThread currentThread]);
}
-(void)handleNotifi:(NSNotification*)notif{
NSLog(@"接收到通知了:%@",[NSThread currentThread]);
}
打印結果:
/*
2017-08-30 14:48:58.916 通知的底層解析[1422:94722] 發送通知before:<NSThread: 0x600000063500>{number = 1, name = main}
2017-08-30 14:48:58.916 通知的底層解析[1422:94722] 接收到通知了:<NSThread: 0x600000063500>{number = 1, name = main}
2017-08-30 14:48:58.917 通知的底層解析[1422:94722] 發送通知after:<NSThread: 0x600000063500>{number = 1, name = main}
*/
以上代碼就是我們平時所寫的發送注冊通知了一般寫法,點擊屏幕發送一次通知,看清楚打印順序,before->處理通知->after
這說明了是消息發完之后要等處理了消息才跑發送消息之后的代碼,這跟多線程中的同步概念相似,所以在同步的通知的方法里不要做耗時的操作,要不然就阻塞主線程
-
使用(同步,異步)
上面介紹了同步,那么我們來說說異步的,就是我發送完之后就繼續跑下面的代碼,不需要去管接受通知的處理
比如異步,那么就要用到通知對列
//同步
-(void)sendNotification{
//每個線程都默認又一個通知隊列,可以直接獲取,也可以alloc
NSNotificationQueue * notificationQueue = [NSNotificationQueue defaultQueue];
NSNotification * notification = [NSNotification notificationWithName:@"EdisonNotif" object:nil];
//NSNotification * notificationTwo = [NSNotification notificationWithName:@"EdisonNotif" object:nil];
/*
(NSPostingStyle) 什么時候發送
NSPostWhenIdle = 1,//空閑時發送
NSPostASAP = 2,//盡快發送
NSPostNow = 3,//現在發送
<#(NSNotificationCoalescing)#>消息合并的方式
NSNotificationNoCoalescing = 0, //不合并
NSNotificationCoalescingOnName = 1,//按名稱合并
NSNotificationCoalescingOnSender = 2,//按發送者合并
*/
NSLog(@"異步發送通知before:%@",[NSThread currentThread]);
[notificationQueue enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil];
//[notificationQueue enqueueNotification:notificationTwo postingStyle:NSPostNow coalesceMask:NSNotificationCoalescingOnName forModes:nil];
//[notificationQueue enqueueNotification:notification postingStyle:NSPostNow coalesceMask:NSNotificationCoalescingOnName forModes:nil];
NSLog(@"異步發送通知after:%@",[NSThread currentThread]);
/*
2017-08-30 15:43:13.068 通知的底層解析[4206:132048] 異步發送通知before:<NSThread: 0x60000007e180>{number = 1, name = main}
2017-08-30 15:43:13.068 通知的底層解析[4206:132048] 異步發送通知after:<NSThread: 0x60000007e180>{number = 1, name = main}
2017-08-30 15:43:13.069 通知的底層解析[4206:132048] 接收到通知了:<NSThread: 0x60000007e180>{number = 1, name = main}
*/
}
如果把發送通知的方法sendNotification改成如上,打印的結果就是表明是異步發送了
再看下通知隊列跟線程的了關系,把點擊屏幕直接發送通知,改成開啟一個線程發送通知
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[NSThread detachNewThreadSelector:@selector(sendAsyncNotification) toTarget:self withObject:nil];
/*
2017-08-30 15:51:17.254 通知的底層解析[4244:137397] 異步發送通知before:<NSThread: 0x600000079480>{number = 3, name = (null)}
2017-08-30 15:51:17.255 通知的底層解析[4244:137397] 異步發送通知after:<NSThread: 0x600000079480>{number = 3, name = (null)}
*/
}
看打印結果,發現根本沒有處理通知
再改下代碼,用線程發送同步的通知中心
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//[self sendNotification];
[NSThread detachNewThreadSelector:@selector(sendNotification) toTarget:self withObject:nil];
//[self sendAsyncNotification];
}
/*
2017-08-30 15:54:49.932 通知的底層解析[4263:139861] 發送通知before:<NSThread: 0x60000026bc40>{number = 3, name = (null)}
2017-08-30 15:54:49.933 通知的底層解析[4263:139861] 接收到通知了:<NSThread: 0x60000026bc40>{number = 3, name = (null)}
2017-08-30 15:54:49.933 通知的底層解析[4263:139861] 發送通知after:<NSThread: 0x60000026bc40>{number = 3, name = (null)}
*/
而用線程發送同步的是可以接受到通知的,并且處理也是在線程里處理的
,這說通知隊列跟線程是有關系的,再繼續改代碼,回到線程發送異步通知,只是把發送時機改成馬上發送
[notificationQueue enqueueNotification:notification postingStyle:NSPostNow coalesceMask:NSNotificationNoCoalescing forModes:nil];
/*
2017-08-30 15:59:19.426 通知的底層解析[4276:143034] 異步發送通知before:<NSThread: 0x60800006a180>{number = 3, name = (null)}
2017-08-30 15:59:19.426 通知的底層解析[4276:143034] 接收到通知了:<NSThread: 0x60800006a180>{number = 3, name = (null)}
2017-08-30 15:59:19.426 通知的底層解析[4276:143034] 異步發送通知after:<NSThread: 0x60800006a180>{number = 3, name = (null)}
*/
這又能處理通知,所以可以說NSPostNow就是同步,其實呢,[[NSNotificationCenter defaultCenter]通知中心這句代碼的意思就是:你在哪個線程里面就是獲取當前線程的通知隊列并且默認采用NSPostNow發送時機
NSLog(@"異步發送通知before:%@",[NSThread currentThread]);
[notificationQueue enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationNoCoalescing forModes:nil];
NSLog(@"異步發送通知after:%@",[NSThread currentThread]);
那么通知隊列到底和線程有什么關系呢:每個線程都有一個通知隊列,當線程結束了,通知隊列就被釋放了,所以當前選擇發送時機為NSPostWhenIdle時也就是空閑的時候發送通知,通知隊列就已經釋放了,所以通知發送不出去了
如果線程不結束,就可以發送通知了,用runloop讓線程不結束
//異步
-(void)sendAsyncNotification{
//每個線程都默認又一個通知隊列,可以直接獲取,也可以alloc
NSNotificationQueue * notificationQueue = [NSNotificationQueue defaultQueue];
NSNotification * notification = [NSNotification notificationWithName:@"EdisonNotif" object:nil];
NSLog(@"異步發送通知before:%@",[NSThread currentThread]);
[notificationQueue enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationNoCoalescing forModes:nil];
NSLog(@"異步發送通知after:%@",[NSThread currentThread]);
NSPort * port = [NSPort new];
[[NSRunLoop currentRunLoop] addPort:port forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
}
/*
2017-08-30 16:12:55.098 通知的底層解析[4394:153794] 異步發送通知before:<NSThread: 0x600000070200>{number = 3, name = (null)}
2017-08-30 16:12:55.098 通知的底層解析[4394:153794] 異步發送通知after:<NSThread: 0x600000070200>{number = 3, name = (null)}
2017-08-30 16:12:55.099 通知的底層解析[4394:153794] 接收到通知了:<NSThread: 0x600000070200>{number = 3, name = (null)}
*/
這樣通知就被發送出去了,而且發送和處理也在線程中,這還沒有達到真正的異步是吧,應該發送在一個線程,處理在另一個線程
再來看下消息合并
//異步
-(void)sendAsyncNotification{
//每個線程都默認又一個通知隊列,可以直接獲取,也可以alloc
NSNotificationQueue * notificationQueue = [NSNotificationQueue defaultQueue];
NSNotification * notification = [NSNotification notificationWithName:@"EdisonNotif" object:nil];
NSNotification * notificationtwo = [NSNotification notificationWithName:@"EdisonNotif" object:nil];
/*
<#(NSNotificationCoalescing)#>消息合并的方式
NSNotificationNoCoalescing = 0, //不合并
NSNotificationCoalescingOnName = 1,//按名稱合并
NSNotificationCoalescingOnSender = 2,//按發送者合并
*/
NSLog(@"異步發送通知before:%@",[NSThread currentThread]);
[notificationQueue enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil];
[notificationQueue enqueueNotification:notificationtwo postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil];
[notificationQueue enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil];
NSLog(@"異步發送通知after:%@",[NSThread currentThread]);
NSPort * port = [NSPort new];
[[NSRunLoop currentRunLoop] addPort:port forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
}
/*
2017-08-30 16:22:16.149 通知的底層解析[4407:159094] 異步發送通知before:<NSThread: 0x608000265640>{number = 8, name = (null)}
2017-08-30 16:22:16.154 通知的底層解析[4407:159094] 異步發送通知after:<NSThread: 0x608000265640>{number = 8, name = (null)}
2017-08-30 16:22:16.154 通知的底層解析[4407:159094] 接收到通知了:<NSThread: 0x608000265640>{number = 8, name = (null)}
*/
設置成NSNotificationCoalescingOnName按名稱合并,此時我連續發送三條,但是只處理了一次,
再繼續,上面代碼就只是把發送時機改成NSPostNow
[notificationQueue enqueueNotification:notification postingStyle:NSPostNow coalesceMask:NSNotificationCoalescingOnName forModes:nil];
[notificationQueue enqueueNotification:notificationTwo postingStyle:NSPostNow coalesceMask:NSNotificationCoalescingOnName forModes:nil];
[notificationQueue enqueueNotification:notification postingStyle:NSPostNow coalesceMask:NSNotificationCoalescingOnName forModes:nil];
/*
2017-08-30 16:30:04.114 通知的底層解析[4442:164620] 異步發送通知before:<NSThread: 0x600000269a40>{number = 3, name = (null)}
2017-08-30 16:30:04.115 通知的底層解析[4442:164620] 接收到通知了:<NSThread: 0x600000269a40>{number = 3, name = (null)}
2017-08-30 16:30:04.115 通知的底層解析[4442:164620] 接收到通知了:<NSThread: 0x600000269a40>{number = 3, name = (null)}
2017-08-30 16:30:04.115 通知的底層解析[4442:164620] 接收到通知了:<NSThread: 0x600000269a40>{number = 3, name = (null)}
2017-08-30 16:30:04.116 通知的底層解析[4442:164620] 異步發送通知after:<NSThread: 0x600000269a40>{number = 3, name = (null)}
*/
結果就打印了處理了三次通知,這個應該好理解吧,就跟dispatch_sync原理一樣,就是得發送因為NSPostNow是同步的,所以發送第一條通知,得等處理完第一條通知,才跑發送第二條通知,這樣肯定就沒有合并消息一說了,因為這有點類似線程阻塞的意思,只有異步,就是三個發送通知全部跑完,在處理通知的時候看是否需要合并和怎么合并,再去處理
那么系統哪些消息是合并的
drawRect
-
通知隊列(線程關系,消息合并等,系統哪些消息做了合并)
-
底層通信port(同線程處理,不同線程處理)
通知隊列也可以實現異步,但是真正的異步還是得通過port
NSPort,底層所有的消息觸發都是通過端口來進行操作的
- (void)viewDidLoad {
[super viewDidLoad];
//[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotifi:) name:@"EdisonNotif" object:nil];
_port =[[NSPort alloc] init];
//消息處理通過代理來處理的
_port.delegate=self;
//把端口加在哪個線程里,就在哪個線程進行處理,下面:加在當前線程的runloop里
[[NSRunLoop currentRunLoop] addPort:_port forMode:NSRunLoopCommonModes];
}
//發送消息
-(void)sendPort{
NSLog(@"port發送通知before:%@",[NSThread currentThread]);
[_port sendBeforeDate:[NSDate date] msgid:1212 components:nil from:nil reserved:0];
NSLog(@"port發送通知after:%@",[NSThread currentThread]);
}
//處理消息
- (void)handlePortMessage:(NSPortMessage *)message{
NSLog(@"port處理任務:%@",[NSThread currentThread]);
NSObject * messageObj = (NSObject*)message;
NSLog(@"=%@",[messageObj valueForKey:@"msgid"]);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self sendPort];
}
/*
2017-08-30 17:09:28.715 通知的底層解析[8171:198651] port發送通知before:<NSThread: 0x600000075440>{number = 1, name = main}
2017-08-30 17:09:28.715 通知的底層解析[8171:198651] port發送通知after:<NSThread: 0x600000075440>{number = 1, name = main}
2017-08-30 17:09:28.715 通知的底層解析[8171:198651] port處理任務:<NSThread: 0x600000075440>{number = 1, name = main}
2017-08-30 17:09:28.715 通知的底層解析[8171:198651] =1212
*/
這樣顯然全部都在一個線程里,修改下代碼
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[NSThread detachNewThreadSelector:@selector(sendPort) toTarget:self withObject:nil];
/*
*/
}
-
分析層次結構(通知中心)