消息通知机制NSNotification

首先,我们经常会遇到这样的一样情况.就是需要监听某个对象,或者通知某个对象去做什么是事情.之前我们经常会使用Delegate.比如我们之前使用的UITableViewDelegate.但是有一个很严重的问题就是,代理的方式是1对1的.对象A只能遵守对象B的协议,然后对象B去调用对象A的方法.这样的方式明显不能满足我们的使用要求,所以苹果为我们提供了另外一种响应方式,那就是NSNotification.系统中(UIKeyboardDidShowNotification 等) 以及某些第三方组件(例如 ASIHTTPRequest 的 kReachabilityChangedNotification 等)。

  • 共同点
  • 利用通知和代理都能完成对象之间的通信(比如A对象告诉D对象发生了什么事情, A对象传递数据给D对象)

  • 不同点

  • 代理 : 一对一关系(1个对象只能告诉另1个对象发生了什么事情)Ø通知 : 多对多关系(1个对象能告诉N个对象发生了什么事情, 1个对象能得知N个对象发生了什么事情)
     

notificationcenter

如何去理解通知中心呢?

我们可以把通知通知中心视为一个求职网站,并且整个社会只有这个么一个求职网站”拉钩网”.

而公司为通知的发布者

求职者为通知的接收者.
下面我们要引入NSNotification,一个完整的NSNotification通常还有2-3个属性.

一个完整的通知一般包含3个属性:

  • (NSString *)name; // 通知的名称(可以理解为岗位名称)

  • (id)object; // 通知发布者(可以理解为公司)

  • (NSDictionary *)userInfo; // 一些额外的信息(比如职位的描述,能力的要求.以字典的方式存放) <而这个额外的信息是可有可无的.

 

初始化一个通知(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;

    通知中心(NSNotificationCenter)调用方法来发布通知:
    - (void)postNotification:(NSNotification )notification;

    当然,我们可以把上面的2部合为一步,就是注册的时候就发布内容,然后再填写发布什么内容,自己的公司情况. 于是:
    - (void)postNotificationName:(NSString )aName object:(id)anObject;
    发布一个名称为aName的通知,anObject为这个通知的发布者
  • (void)postNotificationName:(NSString )aName object:(id)anObject userInfo:(NSDictionary )aUserInfo;
    发布一个名称为aName的通知,anObject为这个通知的发布者,aUserInfo为额外信息

这样我们就成功注册了一个消息的发布者,并且发布了一条内容,但是有一个问题是,我们没有对象来接受,或者响应这个通知.其实这个消息的接受者更准确的讲应该叫消息的观察者.因为:应该先有观察者,再有消息发布者.换句话说,观察者应该先在发布者之前执行.

是这样来注册一个消息的观察者:

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;
  • observer:监听器,即谁要接收这个通知 (求职者名字)
  • aSelector:收到通知后,回调监听器的这个方法,并且把通知对象当做参数传入(收到消息后是要打电话,还是面试,还是怎样)
  • aName:通知的名称。如果为nil,那么无论通知的名称是什么,监听器都能收到这个通知(可以理解为对应上面的岗位名称)
  • anObject:通知发布者。如果为anObject和aName都为nil,监听器都收到所有的通知(求职的公司)
(还有一个因为涉及到多线程,我们今天暂时不讲解的方法):
- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block;
  • name:通知的名称
  • obj:通知发布者
  • block:收到对应的通知时,会回调这个block
  • queue:决定了block在哪个操作队列中执行,如果传nil,默认在当前操作队列中同步执行

    注销观察者(取消注册的监听器)

通知中心不会保留(retain)监听器对象,在通知中心注册过的对象,必须在该对象释放前取消注册。否则,当相应的通知再次出现时,通知中心仍然会向该监听器发送消息。因为相应的监听器对象已经被释放了,所以可能会导致应用崩溃.

如果一个求职者在拉勾网选中了应聘哪个公司的哪个岗位,或者所有公司的哪个岗位,或者哪个公司的所有岗位后,并且已经找到了一个工作.但是没有去拉勾网注明,或者把自己的求职删除.拉勾网就会不断给你推荐不同的岗位.

这样就会出现一个情况,明明你已经没有求职了,但是拉勾网还在给你发送应聘消息.
通知中心提供了相应的方法来取消注册监听器:

- (void)removeObserver:(id)observer;

  • (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;

    一般在监听器销毁之前取消注册(如在监听器中加入下列代码):
    - (void)dealloc {
    //[super dealloc]; 非ARC中需要调用此句
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    }

    Cocoa 还给我们另一种事件通知模型就是 KVO(Key-Value Obsering),基于 NSKeyValueObserving 非正式协议。

参考:

  1. NSNotification Class Reference

  2. Key-Value Observing Programming Guide