我眼中的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 <objc/runtime.h>

@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. 移除通知时,即遍历这个注册表,当对象一致时,从中移除.
    至于实现细节比如多线程安全处理,判断,查找优化肯定也是值得考究的.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容

  • 面试题参考1 : 面试题[http://www.cocoachina.com/ios/20150803/12872...
    江河_ios阅读 1,739评论 0 4
  • NSNotificationCenter对象(通知中心)提供了在程序中广播消息的机制,它实质上就是一个通知分发表。...
    9de75b652cd9阅读 755评论 0 1
  • 还没落笔,脑海里就突然涌出了那时夏日午后漫天飞舞的蜻蜓,好像突然就窥见了那时多愁善感的自己,仿佛那个时候的...
    苓芏阅读 818评论 6 32
  • “老师,你怎么在这?”任火华发现好久没有见到的化学老师坐在自己的办公桌前,似乎在看着什么教辅书。他穿着平时的衣服背...
    糖糖君阅读 404评论 0 0
  • 微风渐起, 有点想你, 想你曾经的样子, 想曾经一起的日子。 我曾经构建了另一个世界, 在那里啊, 你眼里微微闪动...
    旧城丶雨忆阅读 335评论 2 2