一个NSNotificationCenter
对象(通知中心)提供了在程序中广播消息的机制,它实质上就是一个通知分发表。这个分发表负责维护为各个通知注册的观察者,并在通知到达时,去查找相应的观察者,将通知转发给他们进行处理。
本文主要了整理了一下NSNotificationCenter
的使用及需要注意的一些问题,并提出了一些未解决的问题,希望能在此得到解答。
获取通知中心
每个程序都会有一个默认的通知中心。为此,NSNotificationCenter
提供了一个类方法来获取这个通知中心:
1 |
+ (NSNotificationCenter *)defaultCenter |
获取了这个默认的通知中心对象后,我们就可以使用它来处理通知相关的操作了,包括注册观察者,移除观察者、发送通知等。
通常如果不是出于必要,我们一般都使用这个默认的通知中心,而不自己创建维护一个通知中心。
添加观察者
如果想让对象监听某个通知,则需要在通知中心中将这个对象注册为通知的观察者。早先,NSNotificationCenter
提供了以下方法来添加观察者:
1 2 3 4 |
- (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender |
这个方法带有4个参数,分别指定了通知的观察者、处理通知的回调、通知名及通知的发送对象。这里需要注意几个问题:
notificationObserver
不能为nil。notificationSelector
回调方法有且只有一个参数(NSNotification
对象)。- 如果
notificationName
为nil,则会接收所有的通知(如果notificationSender
不为空,则接收所有来自于notificationSender
的所有通知)。如代码清单1所示。 - 如果
notificationSender
为nil,则会接收所有notificationName
定义的通知;否则,接收由notificationSender
发送的通知。 - 监听同一条通知的多个观察者,在通知到达时,它们执行回调的顺序是不确定的,所以我们不能去假设操作的执行会按照添加观察者的顺序来执行。
对于以上几点,我们来重点关注一下第3条。以下代码演示了当我们的notificationName
设置为nil时,通知的监听情况。
代码清单1:添加一个Observer,其中notificationName为nil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:nil object:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:TEST_NOTIFICATION object:nil]; } - (void)handleNotification:(NSNotification *)notification { NSLog(@"notification = %@", notification.name); } @end |
运行后的输出结果如下:
1 2 3 4 5 6 7 8 9 10 |
notification = TestNotification notification = UIWindowDidBecomeVisibleNotification notification = UIWindowDidBecomeKeyNotification notification = UIApplicationDidFinishLaunchingNotification notification = _UIWindowContentWillRotateNotification notification = _UIApplicationWillAddDeactivationReasonNotification notification = _UIApplicationDidRemoveDeactivationReasonNotification notification = UIDeviceOrientationDidChangeNotification notification = _UIApplicationDidRemoveDeactivationReasonNotification notification = UIApplicationDidBecomeActiveNotification |
可以看出,我们的对象基本上监听了测试程序启动后的所示消息。当然,我们很少会去这么做。
而对于第4条,使用得比较多的场景是监听UITextField
的修改事件,通常我们在一个ViewController
中,只希望去监听当前视图中的UITextField
修改事件,而不希望监听所有UITextField
的修改事件,这时我们就可以将当前页面的UITextField
对象指定为notificationSender
。
在iOS 4.0之后,NSNotificationCenter
为了跟上时代,又提供了一个以block
方式实现的添加观察者的方法,如下所示:
1 2 3 4 |
- (idNSObject>)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block |
大家第一次看到这个方法时是否会有这样的疑问:观察者呢?参数中并没有指定具体的观察者,那谁是观察者呢?实际上,与前一个方法不同的是,前者使用一个现存的对象作为观察者,而这个方法会创建一个匿名的对象作为观察者(即方法返回的id
对象),这个匿名对象会在指定的队列(queue
)上去执行我们的block
。
这个方法的优点在于添加观察者的操作与回调处理操作的代码更加紧凑,不需要拼命滚动鼠标就能直接找到处理代码,简单直观。这个方法也有几个地方需要注意:
name
和obj
为nil时的情形与前面一个方法是相同的。- 如果
queue
为nil,则消息是默认在post
线程中同步处理,即通知的post
与转发是在同一线程中;但如果我们指定了操作队列,情况就变得有点意思了,我们一会再讲。 block
块会被通知中心拷贝一份(执行copy
操作),以在堆中维护一个block
对象,直到观察者被从通知中心中移除。所以,应该特别注意在block
中使用外部对象,避免出现对象的循环引用,这个我们在下面将举例说明。- 如果一个给定的通知触发了多个观察者的
block
操作,则这些操作会在各自的Operation Queue
中被并发执行。所以我们不能去假设操作的执行会按照添加观察者的顺序来执行。 - 该方法会返回一个表示观察者的对象,记得在不用时释放这个对象。
下面我们重点说明一下第2点和第3点。
关于第2点,当我们指定一个Operation Queue
时,不管通知是在哪个线程中post
的,都会在Operation Queue
所属的线程中进行转发,如代码清单2所示:
代码清单2:在指定队列中接收通知
1 2 3 4 5 6 7 8 rayon-striped-num" data-line="crayon-5812ce0c1b6d8907061857-4">4 5 6 7 8 p>
本文主要了整理了一下 获取通知中心每个程序都会有一个默认的通知中心。为此,
获取了这个默认的通知中心对象后,我们就可以使用它来处理通知相关的操作了,包括注册观察者,移除观察者、发送通知等。 通常如果不是出于必要,我们一般都使用这个默认的通知中心,而不自己创建维护一个通知中心。 添加观察者如果想让对象监听某个通知,则需要在通知中心中将这个对象注册为通知的观察者。早先,
这个方法带有4个参数,分别指定了通知的观察者、处理通知的回调、通知名及通知的发送对象。这里需要注意几个问题:
对于以上几点,我们来重点关注一下第3条。以下代码演示了当我们的 代码清单1:添加一个Observer,其中notificationName为nil
运行后的输出结果如下:
可以看出,我们的对象基本上监听了测试程序启动后的所示消息。当然,我们很少会去这么做。 而对于第4条,使用得比较多的场景是监听 在iOS 4.0之后,
大家第一次看到这个方法时是否会有这样的疑问:观察者呢?参数中并没有指定具体的观察者,那谁是观察者呢?实际上,与前一个方法不同的是,前者使用一个现存的对象作为观察者,而这个方法会创建一个匿名的对象作为观察者(即方法返回的 这个方法的优点在于添加观察者的操作与回调处理操作的代码更加紧凑,不需要拼命滚动鼠标就能直接找到处理代码,简单直观。这个方法也有几个地方需要注意:
下面我们重点说明一下第2点和第3点。 关于第2点,当我们指定一个 代码清单2:在指定队列中接收通知 |