博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
NSNotification的坑
阅读量:2396 次
发布时间:2019-05-10

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

转载自:  http://yangjunsss.github.io/ios/2014/06/23/Notification%E7%9A%84%E5%9D%91.html

在事件驱动的消息处理中,Notification用起来很方便。 坑一:NSNotificationQueue的addObserver方式是[NSNotificationCenter defaultCenter] addObserver,而不是[NSNotificationQueue defaultQueue] addObserver Notification提供异步post方式NSNotificationQueue,通过enqueueNotification的接口把Notification入队列,并且提供NSPostASAP, NSPostWhenIdle, and NSPostNow这3种时刻来执行,意思分别为在runloop结束时,在线程idle时和立刻。比如:

...[[NSNotificationQueue defaultQueue] addObserver:self selector:@selector(doIdleTask) name:@"idleTask" object:nil]; // 错误,但编译正确不报错- -[[NSNotificationQueue defaultQueue] enqueueNotification:[NSNotification notificationWithName:@"idleTask" object:self] postingStyle:NSPostWhenIdle];...- (void)doIdleTask{
NSLog(@"do task in idle"); [[NSNotificationQueue defaultQueue] enqueueNotification:[NSNotification notificationWithName:@"idleTask" object:self] postingStyle:NSPostWhenIdle];}

可以重复利用当前线程Idle来碎片化执行任务。

在addObserver的时候很容易写成:[[NSNotificationQueue defaultQueue] addObserver:self selector:@selector(doIdleTask) name:@"idleTask" object:nil];,而且这样写并不报错,因为[NSNotificationQueue defaultQueue]返回的是id,在编译期它能是任意的对象,所以能关联上任意子类的方法,从而都没有编译错误,但事实上NSNotificationQueue并没有addObserver方法,所有Notification的addObserver都使用[NSNotificationCenter defaultCenter]。正确的写法是:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doIdleTask) name:@"idleTask" object:nil];

坑二:Notification原则上是不支持多线程的,post在哪个线程,observer就在哪个线程接收,所以别以为网络线程要刷新UI的时候去post,然后执行UI操作,这个坑会导致crash。

Apple提供了一种比较粗糙的实现方式来支持多线程,实质上是进行了传递,“传递者”实现如下:

#import 
@interface NotificationTransfer : NSObject
@property (nonatomic) NSMutableArray *notifications;@property (nonatomic) NSThread *thread;@property (nonatomic) NSLock *lock;@property (nonatomic) NSMachPort *port;- (id) init;- (void) setUpThreadingSupport;- (void) handleMachMessage:(void *)msg;- (void) processNotification:(NSNotification *)notification;@end#import "NotificationTransfer.h"@implementation NotificationTransfer- (id) init{
if (self = [super init]) {
} return self;}- (void) setUpThreadingSupport{
if (self.notifications) {
return; } self.notifications = [NSMutableArray new]; self.lock = [NSLock new]; self.thread = [NSThread currentThread]; self.port = [NSMachPort new]; [self.port setDelegate:self]; [[NSRunLoop currentRunLoop] addPort:self.port forMode:(__bridge NSString *) kCFRunLoopCommonModes];}- (void) handleMachMessage:(void *)msg{
[self.lock lock]; while ([self.notifications count]) {
NSNotification *notification = [self.notifications objectAtIndex:0]; [self.notifications removeObjectAtIndex:0]; [self.lock unlock]; [self processNotification:notification]; [self.lock lock]; } [self.lock unlock];}- (void) processNotification:(NSNotification *)notification{
NSThread *ct = [NSThread currentThread]; if (ct != _thread) {
[self.lock lock]; [self.notifications addObject:notification]; [self.lock unlock]; [self.port sendBeforeDate:[NSDate date] components:nil from:nil reserved:0]; }else{
NSLog(@"process notification %@,is main %zd",[NSThread currentThread],[NSThread isMainThread]); }}@end

processNotification会接收post子线程的notification,然后发现不是当前注册的thread就通过schedule一个port并缓存Notification,最后会执行handleMachMessage从而调用逻辑函数,所以这样做需要注册一个NSThread对象,需要统一定义好事件的接收selector。

调用的code:

- (void)viewDidLoad{
[super viewDidLoad]; _transfer = [NotificationTransfer new]; [_transfer setUpThreadingSupport]; // 在主线程定义了_transfer [[NSNotificationCenter defaultCenter] addObserver:_transfer selector:@selector(processNotification:) name:@"notifi" object:nil]; // 添加了selector dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"notifi" object:nil]; // 子线程post,最终Notification会转发到主线程执行processNotification });}
你可能感兴趣的文章
Flask-SQlAIchemy管理数据库
查看>>
Flask-Migrate实现数据库迁移
查看>>
su: cannot set user id: Resource temporarily unavailable
查看>>
SSHException: Incompatible ssh peer (no acceptable kex algorithm)
查看>>
shell切换用户
查看>>
session机制详解
查看>>
《算法导论》学习总结——第二部分1堆排序
查看>>
linux下进程的一些总结
查看>>
强大的g++呢还是强大的C++?太假了吧
查看>>
C++中的内联函数inline总结
查看>>
C++中的函数指针的一些总结
查看>>
ubuntu下为postgresql添加ODBC驱动过程
查看>>
linux下的su,su -,以及cd,cd - ,cd ~总结
查看>>
Argument of type '(Foo::)(int,int)' does not match 'void (*)(int,int)'以及静态函数问题
查看>>
今天遇到的postgresql中的备份和恢复
查看>>
今天又搞到个libDTL.so is not an ELF file - it has the wrong magic bytes at the start.
查看>>
MinGW和vc6中编译DTL的过程
查看>>
Fedora13下为postgresql添加ODBC驱动过程
查看>>
Bridge模式学习
查看>>
Virtual的一些总结
查看>>