NSNotification的坑

转载自:  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 <Foundation/Foundation.h>

@interface NotificationTransfer : NSObject <NSMachPortDelegate>
@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
    });
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值