notification这东西比较nb的地方在于程序间低耦合,达到了通过name就可以调用八杆子都达不到的方法。但是带来的坏处就是程序不容易维护,有人会说低耦合不就是带来程序的扩展的易于维护吗?我说的不易维护主要集中在name上面,比如程序中大量的使用notificatioin,那么name就很容易冲突,并且如果冲突了是很难发现了,IDE不会画出一到红线告诉你的,当然如果程序规范好了是很容易解决这个问题的。notification的性能没有普通的方法调用(消息发送)高。所以最好是有“广播”需求的时候再用吧,以及特殊需求时再用吧。
NSNotification包含3个信息:name,发送者对象,userinfo。它只能在相同的线程中传递。如果想要在不同线程中传递还需要借助信号量。在相同线程中传递通知是同步的,但是可以借助NSNotificationQueue达到异步的效果。NSNotificationQueue也必须借助run loop达到在同一个线程中的异步效果。
这里enqueueNotification:postingStyle:coalesceMask:forModes方法的最后一个参数指定了runloop的modle。说明消息以什么modle放入runloop中。coalesceMask参数指名了notify放入runloop时,如果runloop中已经有相同的name的notify了,就忽略这次的notify,也就是合并。
我们连续发送了5次通知,发现,只接收到了一次。
通过以上代码,我们在一个notification放入队列准备在runloop中发送完后,添加下一个notifivation对象。这里不用sleep的原因是,sleep会阻塞整个线程。主线程中的所有task都不能运行了。由于图形界面也中主线程中处理,所以你会发现阻塞的过程中,屏幕是黑的。使用[NSRunLoop runUntilDate]方法在运行中是阻塞的,而且它是用来运行run loop 中的task的。所以它即保证了阻塞任务迟点放入run loop,又能让run loop中的任务正常运行。
对应notify不能在线程间传递,官方给出了解决办法:
当执行上面的方法后,任何被发送到notificationPort的消息,都被当前runloop接收,如果当前的线程的run loop没有运行,内核会保存这个消息直到run loop运行。然后run loop会将消息传递到代理方法handleMachMessage方法中。
上面的代码,是当接收到马赫信号时,遍历notification list,并将通知传递给处理方法。
通知处理方法,首先判断这个通知是否是本线程的,如果是就处理通知如果不是,就把通知对象放入队列,并发送马赫信号。
通知的监听,只需要这前面加上安装程序即可。
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];
通知的监听,只需要这前面加上安装程序即可。