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