博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
NSNotificationCenter 和 NSNotificationQueue 使用
阅读量:4297 次
发布时间:2019-05-27

本文共 5309 字,大约阅读时间需要 17 分钟。

NSNotificationCenter 和 NSNotificationQueue

通知中心和通知队列,需要明确的是队列只是缓存通知,并不实际推送通知,但是根据 NSPostingStyleNSNotificationCoalescing 的选择,通知队列可以决定向通知中心传递通知的时机以及是否合并相关的通知。

默认通知队列

NSNotificationQueue 提供了一个类属性 defaultQueue 来获取当前线程默认的通知队列,该队列相关联的通知中心是应用默认的通知中心,并且提供了下面的方法来向队列中添加通知。

- (void)enqueueNotification:(NSNotification *)notification               postingStyle:(NSPostingStyle)postingStyle               coalesceMask:(NSNotificationCoalescing)coalesceMask                   forModes:(nullable NSArray
*)modes;

在使用这个方法时,需要知道的是,使用 NSNotificationCenter 直接推送通知时,通知的执行线程是推送通知的线程,知道这一点对通知队列的理解和使用十分重要。

因为使用上面的方法向队列中添加通知时,涉及到运行循环的模式,而运行循环在子线程中是默认关闭的。所以,除非是在主线程中调用上面的方法,否则需要注意子线程的运行循环是否开启,当然,如果使用 NSPostNow 来表示立即推送通知,那么这和主动调用 NSNotificationCenter 的推送方法效果一样,也不需要考虑运行循环的模式。

若是使用 NSPostASAP(as soon as possible) 和 NSPostWhenIdle 来延后通知的推送,那么就需要考虑到当前线程的运行循环是否开启,以及其是否处于预期的循环模式中。

如下面的例子,如果不开启运行循环,或者开启循环的模式与添加通知时指定的模式不一致时,通知都不会发出。

- (void)viewDidLoad {    [super viewDidLoad];         [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(test:) name:@"test" object:nil];}- (void)btnClick {        dispatch_async(dispatch_get_global_queue(0, 0), ^{        NSNotificationQueue *queue = [NSNotificationQueue defaultQueue];        NSLog(@"%@ %@",NSThread.currentThread,NSRunLoop.currentRunLoop.currentMode);        [NSTimer scheduledTimerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {                        NSLog(@"%@ %@",NSThread.currentThread,NSRunLoop.currentRunLoop.currentMode);        }];        NSNotification *notification = [NSNotification notificationWithName:@"test" object:self];        [queue enqueueNotification:notification postingStyle:NSPostASAP];        notification = [NSNotification notificationWithName:@"test" object:nil];		  /*        [queue enqueueNotification:notification postingStyle:NSPostASAP];                [queue enqueueNotification:notification                      postingStyle:NSPostASAP                      coalesceMask:NSNotificationCoalescingOnName|NSNotificationCoalescingOnSender                          forModes:@[NSRunLoopCommonModes]];		  */        [queue enqueueNotification:notification                      postingStyle:NSPostWhenIdle                      coalesceMask:NSNotificationCoalescingOnName                          forModes:@[NSRunLoopCommonModes]];                        [NSRunLoop.currentRunLoop run];                /*        NSTimer *timer = [NSTimer timerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {            NSLog(@"%@ %@",NSThread.currentThread,NSRunLoop.currentRunLoop.currentMode);        }];        [NSRunLoop.currentRunLoop addTimer:timer forMode:@"MyRunloopMode"];                [NSRunLoop.currentRunLoop runMode:@"MyRunloopMode" beforeDate:NSDate.distantFuture];        */    });}- (void)test:(NSNotification *)notification {    NSLog(@"%@ %@ %@",notification,NSThread.currentThread,NSRunLoop.currentRunLoop.currentMode);}

实际上,使用队列添加通知时,可以根据条件聚合缓存的通知,如果符合聚合条件,那么保留的是已缓存的那个通知,并且判断是否能够聚合的条件并不包括运行循环。这就意味着添加的通知在聚合时,会忽略运行循环的不同,这就需要我们自己考虑其中的影响了。

如下面的例子,在 MyRunloopMode 循环模式下,并没有接收到通知,因为在该循环模式添加的通知根据 NSNotificationCoalescingOnName 聚合条件,同 NSDefaultRunLoopMode 模式下的通知相同,所以通知并不会被添加到队列中。

当 5 秒后,,默认是 NSDefaultRunLoopMode 模式,第一个添加的通知便会发出。

- (void)btnClick {        dispatch_async(dispatch_get_global_queue(0, 0), ^{        NSNotificationQueue *queue = [NSNotificationQueue defaultQueue];        NSLog(@"%@ %@",NSThread.currentThread,NSRunLoop.currentRunLoop.currentMode);                [NSTimer scheduledTimerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {            NSLog(@"%@ %@",NSThread.currentThread,NSRunLoop.currentRunLoop.currentMode);        }];                NSNotification *notification = [NSNotification notificationWithName:@"test" object:self];        [queue enqueueNotification:notification postingStyle:NSPostASAP];                notification = [NSNotification notificationWithName:@"test" object:nil];                NSTimer *timer = [NSTimer timerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {            NSLog(@"%@ %@",NSThread.currentThread,NSRunLoop.currentRunLoop.currentMode);            [queue enqueueNotification:notification                          postingStyle:NSPostASAP                          coalesceMask:NSNotificationCoalescingOnName                              forModes:@[NSRunLoopCommonModes,@"MyRunloopMode"]];        }];        [NSRunLoop.currentRunLoop addTimer:timer forMode:@"MyRunloopMode"];                NSDate *date = [NSDate dateWithTimeIntervalSinceNow:5];        [NSRunLoop.currentRunLoop runMode:@"MyRunloopMode" beforeDate:date];                [NSRunLoop.currentRunLoop run];    });}

自定义通知队列

使用下面的方法自定义通知队列,既然要自定义队列,那么大多也就意味着我们不想使用 NSNotificationCenter 类属性 defaultCenter 返回的默认的通知中心,而是自己指定推送通知队列中的通知的通知中心。

- (instancetype)initWithNotificationCenter:(NSNotificationCenter *)notificationCenter;

实际上,defaultCenter 作为应用默认的通知中心,负责承接所有的系统的通知,也是我们应用中常用的通知中心。但是,这同样意味着接收每一个通知推送任务时,通知中心会去遍历注册表中的所有监听者,判断其是否需要执行相关任务。这对于通知繁多,性能敏感的应用是十分不利的。如果监听者要执行的任务十分耗时,那么便会阻塞当前线程,而该线程若恰好是主线程时,结果就相当不美好了。所以自定义自己的通知中心,分门别类的将通知发送给指定的通知中心去推送,可以提高性能和功能的模块化。

如果对通知的时效性要求较高,则可以配合自定义队列使用,从而过滤掉无效的通知。

每个线程都可以获取到同默认通知中心相关联的通知队列,即说明一个通知中心可以被多个通知队列相关联。但实际上,通知队列和通知中心和线程都是不想关的,即使在子线程中创建通知队列,在主线程中同样可以使用其添加通知。

这里所说的通知中心,是面向开发者的,同面向是两个概念。

转载地址:http://xvdws.baihongyu.com/

你可能感兴趣的文章
更轻量的 View Controllers
查看>>
Chisel-LLDB命令插件,让调试更Easy
查看>>
时间格式化hh:mm:ss和HH:mm:ss区别
查看>>
When to use Delegation, Notification, or Observation in iOS
查看>>
Objective-C Autorelease Pool 的实现原理
查看>>
编程语言大牛王垠:编程的智慧,带你少走弯路
查看>>
ios指令集以及基于指令集的app包压缩策略
查看>>
iOS开发者的福利 — — iOS9+Xcode7免越狱免证书直接调试
查看>>
3、JavaWeb学习之基础篇—JSP
查看>>
4、JavaWeb学习之基础篇—Session
查看>>
5、JavaWeb学习之基础篇—标签(自定义&JSTL)
查看>>
8、JavaWEB学习之基础篇—文件上传&下载
查看>>
reRender属性的使用
查看>>
href="javascript:void(0)"
查看>>
h:panelGrid、h:panelGroup标签学习
查看>>
f:facet标签 的用法
查看>>
<h:panelgroup>相当于span元素
查看>>
java中append()的方法
查看>>
必学高级SQL语句
查看>>
经典SQL语句大全
查看>>