谈谈 NSNotification

    notification这东西比较nb的地方在于程序间低耦合,达到了通过name就可以调用八杆子都达不到的方法。但是带来的坏处就是程序不容易维护,有人会说低耦合不就是带来程序的扩展的易于维护吗?我说的不易维护主要集中在name上面,比如程序中大量的使用notificatioin,那么name就很容易冲突,并且如果冲突了是很难发现了,IDE不会画出一到红线告诉你的,当然如果程序规范好了是很容易解决这个问题的。notification的性能没有普通的方法调用(消息发送)高。所以最好是有“广播”需求的时候再用吧,以及特殊需求时再用吧。
    NSNotification包含3个信息:name,发送者对象,userinfo。它只能在相同的线程中传递。如果想要在不同线程中传递还需要借助信号量。在相同线程中传递通知是同步的,但是可以借助NSNotificationQueue达到异步的效果。NSNotificationQueue也必须借助run loop达到在同一个线程中的异步效果。

消息同步发送:

// 注册监听[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveRemoteRequest:) name:kSendImage object:nil];
// 发送notify
[[NSNotificationCenter defaultCenter] postNotificationName:url.path object:nil userInfo:parameters];


 
初次使用,会感觉这个东西和神秘。一定使用了某种独立服务进程。但是通过查看它的线程栈发现,它的实现只是通过普通代码调用得到。 

上图中,下面的红框是notify发送代码所在位置,上面的红框是接收端使用的代码。说明notify是直接通过方法调用实现的。并且没有进行多线程处理。消息异步发送:

// 注册监听[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveRemoteRequest:) name:kSendImage object:nil];

// 异步发布消息

NSNotification *notification = [[NSNotification alloc] initWithName:url.path object:nil userInfo:parameters];

NSNotificationQueue *notificationQueue = [NSNotificationQueue defaultQueue];

[notificationQueue enqueueNotification:notification postingStyle:NSPostASAP coalesceMask:NSNotificationCoalescingOnSender forModes:nil];


 
这里enqueueNotification:postingStyle:coalesceMask:forModes方法的最后一个参数指定了runloop的modle。说明消息以什么modle放入runloop中。coalesceMask参数指名了notify放入runloop时,如果runloop中已经有相同的name的notify了,就忽略这次的notify,也就是合并。 

for(int i=0;i<5;i++){
        // 异步发布消息
        NSNotification *notification = [[NSNotificationalloc] initWithName:url.pathobject:niluserInfo:parameters];
        NSNotificationQueue *notificationQueue = [NSNotificationQueuedefaultQueue];
        [notificationQueue enqueueNotification:notification postingStyle:NSPostASAPcoalesceMask:NSNotificationCoalescingOnNameforModes:nil];
    }

我们连续发送了5次通知,发现,只接收到了一次。

for(int i=0;i<5;i++){
        NSDate *date = [NSDate date];
        // 这是一个阻塞操作,它和sleep不同的是,sleep单纯的阻塞到指定时间,这个线程内的所以方法都不能运行
        // 而它是阻塞了当前的task,然后执行其他task,等到其他task执行完或者时间到后这个方法返回
        [[NSRunLoop currentRunLoop] runUntilDate:[date dateByAddingTimeInterval:1]];
        //            sleep(1);

        // 异步发布消息
        NSNotification *notification = [[NSNotification alloc] initWithName:url.path object:nil userInfo:parameters];
        NSNotificationQueue *notificationQueue = [NSNotificationQueue defaultQueue];
        [notificationQueue enqueueNotification:notification postingStyle:NSPostASAP coalesceMask:NSNotificationCoalescingOnSender forModes:nil];
    }

通过以上代码,我们在一个notification放入队列准备在runloop中发送完后,添加下一个notifivation对象。这里不用sleep的原因是,sleep会阻塞整个线程。主线程中的所有task都不能运行了。由于图形界面也中主线程中处理,所以你会发现阻塞的过程中,屏幕是黑的。使用[NSRunLoop runUntilDate]方法在运行中是阻塞的,而且它是用来运行run loop 中的task的。所以它即保证了阻塞任务迟点放入run loop,又能让run loop中的任务正常运行。

对应notify不能在线程间传递,官方给出了解决办法:

/* Threaded notification support. */

// 保持notification对象
@property NSMutableArray *notifications;

// 目标线程
@property NSThread *notificationThread;

// 保证notifications的线程安全
@property NSLock *notificationLock;

// 向正确的线程发送信号
@property NSMachPort *notificationPort;

 

- (void) setUpThreadingSupport;

- (void) handleMachMessage:(void *)msg;

- (void) processNotification:(NSNotification *)notification;

@end

- (void) setUpThreadingSupport {
    if (self.notifications) {
        return;
    }
    self.notifications      = [[NSMutableArray alloc] init];
    self.notificationLock   = [[NSLock alloc] init];
    self.notificationThread = [NSThread currentThread];
 
    self.notificationPort = [[NSMachPort alloc] init];
    [self.notificationPort setDelegate:self];
    [[NSRunLoop currentRunLoop] addPort:self.notificationPort
            forMode:(NSString __bridge *)kCFRunLoopCommonModes];
}

当执行上面的方法后,任何被发送到notificationPort的消息,都被当前runloop接收,如果当前的线程的run loop没有运行,内核会保存这个消息直到run loop运行。然后run loop会将消息传递到代理方法handleMachMessage方法中。

- (void) handleMachMessage:(void *)msg {
 
    [self.notificationLock lock];
 
    while ([self.notifications count]) {
        NSNotification *notification = [self.notifications objectAtIndex:0];
        [self.notifications removeObjectAtIndex:0];
        [self.notificationLock unlock];
        [self processNotification:notification];
        [self.notificationLock lock];
    };
 
    [self.notificationLock unlock];
}

上面的代码,是当接收到马赫信号时,遍历notification list,并将通知传递给处理方法。

- (void)processNotification:(NSNotification *)notification {
 
    if ([NSThread currentThread] != notificationThread) {
        // Forward the notification to the correct thread.
        [self.notificationLock lock];
        [self.notifications addObject:notification];
        [self.notificationLock unlock];
        [self.notificationPort sendBeforeDate:[NSDate date]
                components:nil
                from:nil
                reserved:0];
    }
    else {
        // Process the notification here;
    }
}

通知处理方法,首先判断这个通知是否是本线程的,如果是就处理通知如果不是,就把通知对象放入队列,并发送马赫信号。

[self setupThreadingSupport];
[[NSNotificationCenter defaultCenter]
        addObserver:self
        selector:@selector(processNotification:)
        name:@"NotificationName"
        object:nil];

通知的监听,只需要这前面加上安装程序即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值