我眼中的NSNotificationCenter

  • 正确的使用通知方法
  • 通知中心与多线程
  • 通知中心需要释放吗?
  • 如何实现自动移除监听?
  • 通知的实现原理

正确的使用通知方法

问题一

object参数有什么用?

  • 添加通知时,若指定了object参数,那么该响应者会判断object参数是否为同一实例,相同实例的监听者才会收到通知.
  • 发送通知时,若指定了object参数,注册通知时object为nil也是可以收到的.

问题2

usingBlock要注意使用弱引用(并不是循环引用)问题:
NSNotificationCenter是一个单例对象所以其在程序运行期间,不会被释放,内部维护一个注册通知表,key为通知名称,value为注册该通知的对象(包装过)的数组.该对象对block持有引用
NSNotificationCenter ->通知表 ->__NSObserver(通知包装对象) ->Block ->Observer
所以不要在注册通知内部强引用self.

问题3

不要对同一实例对象重复注册一个通知,会导致收到通知时执行多次

通知中心与多线程

官方说明:

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接收和处理都是在发送Notification的线程中的,而不一定是在注册通知的那个线程中。

  • 主线程还是子线程注册的通知,无论在哪个线程发送都是可以得到处理的.
  • 在那个线程发送的通知,就在哪个线程收到这个通知,并进行处理.
  • 主线程发出的通知不要做耗时的操作,导致UI卡顿

通知中心需要释放吗?

一直以来得到的回答都是,需要释放,不释放会造成崩溃,但是我真的没有遇到崩溃的情况啊,有木有?
官方文档:

If your app targets iOS 9.0 and later or macOS 10.11 and later,
 you don't need to unregister an observer in its dealloc method. 
Otherwise, you should call removeObserver:name:object: before observer
 or any object passed to this method is deallocated.

也就是在iOS9之后,就不需要在dealloc移除通知了.
下载iOS8.1的模拟器进行测试不移除通知,发送通知导致崩溃,瞬间感觉神清气爽.

  • iOS9之前通知中心对响应者observer是使用unsafe_unretained修饰,当响应者释放会出现野指针,向野指针发送消息造成崩溃;在iOS9系统之后,苹果对observer使用weak修饰,observer释放变成nil,不会导致崩溃.
    官方文档
NSNotificationCenter and NSDistributedNotificationCenter no longer send notifications
 to registered observers that may be deallocated. If the observer is able to be stored 
as a zeroing-weak reference the underlying storage stores the observer as a zeroing
 weak reference. Alternatively, if the object cannot be stored weakly (because it has 
a custom retain/release mechanism that would prevent the runtime from being able 
to store the object weakly) the object is stored as a non-weak zeroing reference. 
This means that observers are not required to un-register in their deallocation method.

但是对于使用block的方式,还是需要移除的.

The return value is retained by the system, and should be held onto by 
the caller in order to remove the observer with removeObserver: later, to stop observation.

做法是对该注册通知的方法的返回值持一个强引用,在dealloc方法中对其调用removeObserver方法.不然这个__NSObserver对象并没有取消监听,会一直存在,发送通知还是可以收到的.
所以正确的说法应该是如果是通过selector的方式(iOS9.0以上)是不需要移除,使用Block的方式还是需要移除的.
但是我觉得作为程序员有个良好的编程习惯是很重要的,在不需要监听的时候还是进行移除为妙.

如何实现自动移除监听?

对于这个问题大致两种方法

  • 新建第三方对象监听者持有该实例对象(runtime添加关联属性),当监听者释放,这个实例对象不被持有也会执行dealloc方法,在该方法移除通知.
  • runtime交换dealloc方法,移除通知.
    思路一:
#import "AutoRemoveObserver.h"
#import 

@interface AutoRemoveObserver ()
//监听者
@property (nonatomic, weak) id observer;
//传入的object
@property (nonatomic, strong) id object;
//通知名称
@property (nonatomic, copy) NSString *name;
//usingblock返回的对象
@property (nonatomic, strong) id blockObserver;

@end

@implementation AutoRemoveObserver

+ (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject{

//1.创建一个对象
AutoRemoveObserver* remover = [[AutoRemoveObserver alloc] init];
remover.observer = observer;
remover.name = aName;
remover.object = anObject;

//2.为监听者添加一个属性,强引用
objc_setAssociatedObject(observer, (__bridge const void *)(remover), remover, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

// 注册通知
[[NSNotificationCenter defaultCenter] addObserver:observer
selector:aSelector
name:aName
object:anObject];
}

+ (void)addObserver:(id)observer name:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block{

//1.创建一个对象,用于关联其监听者的生命周期
AutoRemoveObserver* remover = [[AutoRemoveObserver alloc] init];

id blockObserver = [[NSNotificationCenter defaultCenter] addObserverForName:name object:obj queue:queue usingBlock:block];

objc_setAssociatedObject(observer, (__bridge const void *)(remover), remover, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

remover.blockObserver = blockObserver;
}

-(void)dealloc{
if ( self.blockObserver) {
// 移除block
[[NSNotificationCenter defaultCenter] removeObserver:self.blockObserver];
}
else {
// Selector
[[NSNotificationCenter defaultCenter] removeObserver:self.object
name:self.name
object:self.object];
}
}
@end


通知的实现原理

相信能坚持看到这里,一定对于通知的实现原理了然于心了吧!
大致思路

  1. 注册通知时,内部创建一个私有对象,对observer持有一个weak引用(block是对block进行强引用,所以需要手动释放),记录SEL,name,object,queue等参数
  2. 通知中心维护一张表,key为通知名称(NSNotificationName),value为数组容器存储内部私有对象,注册一次,添加一个对象.
  3. 发送通知时,根据通知名称去获取内部私有对象数组,进行遍历执行SEL(或者Block),当对象为nil时,向nil发消息不会发生任何事情,但应当把这个对象从注册表中移除(否则无法释放).
  4. 移除通知时,即遍历这个注册表,当对象一致时,从中移除.
    至于实现细节比如多线程安全处理,判断,查找优化肯定也是值得考究的.

你可能感兴趣的:(我眼中的NSNotificationCenter)