1、NSNotification消息的同步性
①NSNotification使用的是同步操作。即如果你在程序中的A位置post了一个NSNotification,在B位置注册了一个observer,通知发出后,必须等到B位置的通知回调执行完以后才能返回到A处继续往下执行。
因此,不要过多的或者低效的使用NSNotification,《Cocoa基本原理指南》一文推荐的方式是通过一些“中间的”观察者将通告的结果传递给它们可以访问的对象。
②如果想让NSNotification的post处和observer处异步执行,可以通过NSNotificationQueue实现。
2、多个观察者的执行顺序
对于同一个通知,如果注册了多个观察者,则这多个观察者的执行顺序和他们的注册顺序是保持一致的。
3、NSNotification通知转发线程
①NSNotificationCenter在转发NSNotification消息的时候,在哪个线程中post,就在哪个线程中转发。换句话说,不管你的observer是在哪个线程,observer的回调方法执行线程都和post的线程保持一致。
②如果想让post的线程和转发的线程不同,可以通过NSNotification重定向技术实现。
4、addObserver和removeObserver必须成对出现
官方文档中是这样描述的:
The notification center does not retain its observers, therefore, you must ensure that you unregister observers (usingremoveObserver: or removeObserver:name:object:) before they are deallocated. (If you don’t, you will generate a runtime error if the center sends a message to a freed object.)
再addObserver的时候,notification center并不增加观察者对象的引用计数,因此,在观察者对象被释放之前我们必须保证它们被从观察队列中移除,否则后果很明显!
5、Notification与多线程
前几天与同事讨论到Notification在多线程下的转发问题,所以就此整理一下。
先来看看官方的文档,是这样写的:
In a multithreaded application, notifications are always delivered in the thread in which the notification was posted, which may not be the same thread in which an observer registered itself.
翻译过来是:
在多线程应用中,Notification在哪个线程中post,就在哪个线程中被转发,而不一定是在注册观察者的那个线程中。
也就是说,Notification的发送与接收处理都是在同一个线程中。为了说明这一点,我们先来看一个示例:
代码清单1:Notification的发送与处理
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"current thread = %@", [NSThread currentThread]);
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:TEST_NOTIFICATION object:nil];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:TEST_NOTIFICATION object:nil userInfo:nil];
});
}
- (void)handleNotification:(NSNotification *)notification
{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"test notification");
}
@end
其输出结果如下:
可以看到,虽然我们在主线程中注册了通知的观察者,但在全局队列中post的Notification,并不是在主线程处理的。所以,这时候就需要注意,如果我们想在回调中处理与UI相关的操作,需要确保是在主线程中执行回调。
这时,就有一个问题了,如果我们的Notification是在二级线程中post的,如何能在主线程中对这个Notification进行处理呢?或者换个提法,如果我们希望一个Notification的post线程与转发线程不是同一个线程,应该怎么办呢?我们看看官方文档是怎么说的:
For example, if an object running in a background thread is listening for notifications from the user interface, such as a window closing, you would like to receive the notifications in the background thread instead of the main thread. In these cases, you must capture the notifications as they are delivered on the default thread and redirect them to the appropriate thread.
这里讲到了“重定向”,就是我们在Notification所在的默认线程中捕获这些分发的通知,然后将其重定向到指定的线程中。
一种重定向的实现思路是自定义一个通知队列(注意,不是NSNotificationQueue对象,而是一个数组),让这个队列去维护那些我们需要重定向的Notification。我们仍然是像平常一样去注册一个通知的观察者,当Notification来了时,先看看post这个Notification的线程是不是我们所期望的线程,如果不是,则将这个Notification存储到我们的队列中,并发送一个信号(signal)到期望的线程中,来告诉这个线程需要处理一个Notification。指定的线程在收到信号后,将Notification从队列中移除,并进行处理。
这种实现方式的具体解析及其局限性大家可以参考官方文档Delivering Notifications To Particular Threads,在此不多做解释。当然,更好的方法可能是我们自己去子类化一个NSNotificationCenter,或者单独写一个类来处理这种转发。
参考