NSNotificationCenter深入研究

来源:互联网 发布:手机淘宝怎么上传图片 编辑:程序博客网 时间:2024/06/07 18:27
  • 原文 :  http://www.jianshu.com/p/26323f5b823d  原文有关于另一种通知的用法, addObserverForName, 但是这个我没用过,就没摘抄 

  • http://www.cocoachina.com/ios/20170426/19124.html

  • 因为通知的接收所在的线程是基于发送通知所在的线程,如果通知是在主线程发出的,通知的接收也是在主线程,如果通知的发送是在子线程,通知的接收也是在子线程。也就是说如果你不能保证你的通知一定是在主线程发送的,就最好回到主线程刷新UI

  • 特别注意事项:通知的移除不一定非要写

对于addObserver:

这里要分ViewController和普通NSObject两个说起
ViewController:在调用ViewController的dealloc的时候,系统会调用[[NSNotificationCenter defaultCenter]removeObserver:self]方法,所以如果是在viewDidLoad中使用addObserver添加监听者的话可以省掉移除。当然,如果是在viewWillAppear中添加的话,那么就要在viewWillDisappear中自己移除,而且,最好是使用[[NSNotificationCenter defaultCenter] removeObserver:self name:@"test" object:nil];而非[[NSNotificationCenter defaultCenter]removeObserver:self],否则很有可能你会把系统的其他通知也移除了

普通NSObject:在iOS9之后,NSObject也会像ViewController一样在dealloc时调用[[NSNotificationCenter defaultCenter]removeObserver:self]方法,在iOS9之前的不会调用,需要自己写。

  但是在使用类别的时候如果我们添加了通知,那么我们是没有办法在类别里面重写dealloc的,如果不移除通知就会出现野指针,这个时候我们就可以在iOS9以上使用addObserver,将通知的移除交给系统,iOS9一下使用addObserverForName+weakSelf,虽然通知依然存在,但是不会调用doSomeThing方法(不要直接在block里面写处理过程啊)。


ViewController、NSObject、Notification释放图表(YES-释放).png

最后附上测试demo下载


疑难点 : 通知发出后会立即调用吗? 还是在开一个新线程异步调用?
同步or异步
同步和异步都是相对于发送通知所在的线程的。可以通过下述代码简单测出:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 替换上述的didFinishLaunchingWithOptions函数
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    A *a = [A new];
    [a test];
    self.a = a;
    // 发送方式A:手动定义NSNotification
    NSNotification *noti = [NSNotification notificationWithName:@"111" object:nil];
    [[NSNotificationCenter defaultCenter] postNotification:noti];
    // 发送方式B:自动定义NSNotification
    [[NSNotificationCenter defaultCenter] postNotificationName:@"111" object:nil userInfo:nil];
    NSLog(@"测试同步还是异步");
    return YES;
}

输出总是为:

1
2
3
4
5
2017-02-26 19:13:04.739 notification[15240:12674807] selector 1
2017-02-26 19:13:04.743 notification[15240:12674807] block 2
2017-02-26 19:13:04.743 notification[15240:12674807] selector 3
2017-02-26 19:13:04.744 notification[15240:12674807] block 4
2017-02-26 19:13:04.744 notification[15240:12674807] 测试同步还是异步
  • 可以看出,postNotification:总是会卡住当前线程,待observer执行(如不特殊处理selector也会在postNotification:所在线程执行)结束之后才会继续往下执行。所以是同步的。



  • 忘记remove的问题
    这个就不进行测试了,因为我也没有iOS8以及之前的设备。直接写结论了:

  • 若在iOS8或之前版本系统中,对一个对象addObserver:selector:name:object:(假设name为@“111”),但是并没有在dealloc的之前或之中,对其进行remove操作。那么,在发送通知(name为@“111”)的时候,会因为bad_access(野指针)而crash。

  • 若在iOS9及以后,同上操作,不会crash。

    iOS8及以前,NSNotificationCenter持有的是观察者的unsafe_unretained指针(可能是为了兼容老版本),这样,在观察者回收的时候未removeOberser,而后再进行post操作,则会向一段被回收的区域发送消息,所以出现野指针crash。而iOS9以后,unsafe_unretained改成了weak指针,即使dealloc的时候未removeOberser,再进行post操作,则会向nil发送消息,所以没有任何问题。


再总结一下

当你的通知没办法注销的时候,iOS9以下addObserverForName可以不注销通知的情况下(通知没释放)释放监听者,iOS9以上使用addObserver可以不手动注销通知情况下释放通知和监听者,涉及到内存使用问题。关键在于没办法手动注销通知的情况下,比如你的扩展里面使用了通知,但是扩展不支持调用dealloc.

原创粉丝点击