本文参考
苹果文档 Notification Programming Topics
本文目录
- 概述
- 1.NSNotification
- 1.1 创建NSNotification对象
- 2.NSNotificationCenter
- 2.1 通知中心的作用
- 2.2 通知中心的分类
- 2.3 NSNotificationCenter 的使用
- 2.4 NSNotificationCenter的方法
- 2.5 注册Observer的方法
- 2.6 发布通知(NSNotification)
- 2.7 注销Observer的方法
- 3.NSNotificationQueue
- 3.1 创建通知队列方法
- 3.2 往队列加入通知(发送通知)方法
- 3.3 移除队列中的通知方法
- 3.4 发送方式
- 3.5 合并通知
- 4.NSNotificatinonCenter实现原理
概述
The standard way to pass information between objects is message passing—one object invokes the method of another object. However, message passing requires that the object sending the message know who the receiver is and what messages it responds to. At times, this tight coupling of two objects is undesirable—most notably because it would join together two otherwise independent subsystems. For these cases, a broadcast model is introduced: An object posts a notification, which is dispatched to the appropriate observers through an NSNotificationCenter object, or simply notification center.
在对象之间传递消息的标准方式是:一个对象调用另一个对象的方法。然而,消息传递要求发送消息的对象需要知道接收者是谁以及谁来响应它,这会使两个独立的子系统紧密地耦合起来。
为了解决这种情况,引入了广播模式:对象发布通知,通过 NSNotificationCenter 来派发通知给观察者。这样,对象根本就不需要知道观察者的存在。
通知和delegate的区别:
- 任何数量的对象都可以接受通知,而不仅仅是delegate对象。也就是说,****通知是多对多的,而delegate是一对一的****。
- 对象可以从 NSNotificationCenter 中接受任何消息,而不仅仅是预定义的代理方法。
- 发布通知的对象不需要知道观察者的存在。
1. NSNotification
NSNotification:它是通知中心向所有观察者发布广播信息的容器。
一个NSNotification对象包含三个属性:
- name:通知的名称
- object:发布通知的对象
- dictionary:这个是可选的,为通知传递的字典信息。
1.1 创建NSNotification对象
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject;
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
- (instancetype)initWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo;
实际上,在发布通知的时候,一般都不会去创建一个通知对象,而是使用通知中心直接发布。
2. NSNotificationCenter
2.1 通知中心的作用
NSNotificationCenter(通知中心)是用来管理通知的接收和发送的。一个对象发送通知给通知中心,然后通知中心会将这个通知发送给观察者。
2.2 通知中心的分类
通知中心有两类:
- NSNotificationCenter:这个是用来管理单个进程上的通知
- NSDistributedNotificationCenter:这个是用来管理一台计算机上多个进程间的通知
2.3 NSNotificationCenter 的使用
一个进程都会有一个默认的通知中心,你可以使用下列方法获得它
[NSNotificationCenter defaultCenter];
2.4 NSNotificationCenter的方法
@property (class, readonly, strong) NSNotificationCenter *defaultCenter;
//注册观察者
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
//发布通知
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
//移除观察者
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject;
注意点:
NSNotificationCenter 会同步传递通知给观察者,也就是说,当一个对象发送通知后,必须等到观察者接收到并处理完这个通知后,这个对象发送通知才会返回;
如果想异步发布通知,使用 NotificationQueue;
在多线程中,通知总是在发布通知的线程中被传递,这个可能与观察者注册的线程不同;
2.5 注册Observer的方法
方法1,SEL方式:
使用以下方法可以向通知中心添加一个Observer
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
参数
- observer:注册为观察者的对象;
- aSelector:观察者收到通知后,回调的方法。这个方法有且仅有一个参数,就是NSNotification对象;
- aName:通知的名称,即观察者要接收的通知的名称。如果aName为nil的话,那么所有的通知到会到达这个观察者;
- anObject:通知的发布者。也就是说,指定了anObject后,只有这个对象发布的通知才会派发给观察者。
方法2,block方式:
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
参数
- name:通知的名称,观察者想要接收的通知名称;
- obj:通知的发布者。也就是说,指定了anObject后,只有这个对象发布的通知才会派发给观察者;
- queue:block被添加进的 NSOperationQueue 队列。如果为空,block将会在发布线程中同步执行;
- block:观察者接收到通知后要执行的任务。
2.6 发布通知(NSNotification)
通知中心(NSNotificationCenter)提供了相应的方法来帮助发布通知。
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
注意:
- 参数anObject为发布通知的对象
- 如果在子线程发布通知,那么也会在子线程通知观察者
- 发布通知为同步操作,只有在观察者接收到并处理完这个通知后,才会返回往下执行。
2.7 注销Observer的方法
通知中心提供了对应的方法来注销观察者。
通知中心不会持有(retain)观察者对象,在通知中心注册过的对象,必须在该对象释放前取消注册。否则,当相应的通知再次出现时,通知中心仍然会向该监听器发送消息。因为相应的监听器对象已经被释放了,所以可能会导致应用崩溃。
方法1:
- (void)removeObserver:(id)observer;
这个方法可以移除观察者对象。但是最好不要在 delloc方法 之前使用,因为这个对象如果是长期存在的话,也可能被其它的代码注册为了观察者。所以要在delloc销毁这个对象的时候,去移除这个观察者对象。
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
方法2:
- (void)removeObserver:(id)observer name:(NSNotificationName)aName object:(id)anObject;
参数
name:通知的名称;
anobject:发布通知的对象。
这个方法可以通过name和anobject参数移除特定的观察者对象。
3. NSNotificationQueue
发布通知是同步执行的,只有在观察者对象接收到并处理完这个通知后才会往下执行。要想异步发布通知,就需要使用NSNotificationQueue。
NSNotificationQueue:通知队列,用来管理多个通知的调用。通知队列通常以先进先出(FIFO)顺序维护。NSNotificationQueue就像一个缓冲池把一个个通知放进池子中,使用特定方式通过NSNotificationCenter发送到相应的观察者。
3.1 创建通知队列方法
- (instancetype)initWithNotificationCenter:(NSNotificationCenter *)notificationCenter NS_DESIGNATED_INITIALIZER;
3.2 往队列加入通知(发送通知)方法
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle;
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle coalesceMask:(NSNotificationCoalescing)coalesceMask forModes:(nullable NSArray<NSRunLoopMode> *)modes;
3.3 移除队列中的通知方法
- (void)dequeueNotificationsMatching:(NSNotification *)notification coalesceMask:(NSUInteger)coalesceMask;
3.4 发送方式
NSPostingStyle包括三种类型:
typedef NS_ENUM(NSUInteger, NSPostingStyle) {
NSPostWhenIdle = 1,
NSPostASAP = 2,
NSPostNow = 3
};
- NSPostWhenIdle:空闲发送通知,当RunLoop处于等待或空闲状态时,发送通知,对于不重要的通知可以使用。
- NSPostASAP:尽快发送通知,当前RunLoop迭代完成时,通知将会被发送,有点类似没有延迟的定时器。
- NSPostNow :同步发送通知,如果不使用合并通知和postNotification:一样是同步通知。
3.5 合并通知
NSNotificationQueue除了有异步通知的能力之外,也能对当前队列的通知根据NSNotificationCoalescing类型进行合成(即将几个合成一个)
NSNotificationCoalescing也包括三种类型:
typedef NS_OPTIONS(NSUInteger, NSNotificationCoalescing) {
NSNotificationNoCoalescing = 0,
NSNotificationCoalescingOnName = 1,
NSNotificationCoalescingOnSender = 2
};
NSNotificationNoCoalescing:不合并通知。
NSNotificationCoalescingOnName:合并相同名称的通知(NSNotification的name相同)。
NSNotificationCoalescingOnSender:合并同一对象的通知(NSNotificationobject相同)。
通过合并我们可以用来保证相同的通知只被发送一次。
forModes:(nullable NSArray<NSRunLoopMode> *)modes
可以使用不同的NSRunLoopMode配合来发送通知,可以看出实际上NSNotificationQueue与RunLoop的机制以及运行循环有关系,通过NSNotificationQueue队列来发送的通知和关联的RunLoop运行机制来进行的。
4. NSNotificatinonCenter实现原理
NSNotificatinonCenter用来管理通知的接收和发送。将观察者注册到NSNotificatinonCenter的通知调度表中,然后发送通知时利用标识符name和object识别出调度表中的观察者,然后调用相应的观察者的方法,即传递消息(在Objective-C中对象调用方法,就是传递消息,消息有name或者selector,可以接受参数,而且可能有返回值),如果是基于block创建的通知就调用NSNotification的block。