iOS NSNotificationCenter 使用姿式详解

最近在作平板的过程当中,发现了一些很不规范的代码。偶然修复支付bug的时候,看到其余项目代码,使用通知的地方没有移除,我觉得我这个模块的支付闪退是由于他通知没有移除的缘故。而在debug和看了具体的代码的时候才发现和这里没有关系。在我印象中,曾经由于没有移除通知而遇到闪退的问题。因此让我很意外,因而写了个demo研究了下,同时来说下NSNotificationCenter使用的正确姿式。多线程


NSNotificationCenter


对于这个不必多说,就是一个消息通知机制,相似广播。观察者只须要向消息中心注册感兴趣的东西,当有地方发出这个消息的时候,通知中心会发送给注册这个消息的对象。这样也起到了多个对象之间解耦的做用。苹果给咱们封装了这个NSNotificationCenter,让咱们能够很方便的进行通知的注册和移除。然而,有些人的姿式仍是有点小问题的,下面就看看正确的姿式吧!app


正确姿式之remove


只要往NSNotificationCenter注册了,就必须有remove的存在,这点是你们共识的。可是你们在使用的时候发现,在UIViewController 中addObserver后没有移除,好像也没有挂!我想不少人可能和我有同样的疑问,是否是由于使用了ARC?在你对象销毁的时候自动置为nil了呢?或者苹果在实现这个类的时候用了什么神奇的方式呢?下面咱们就一步步来探究下。async


首先,向NSNotificationCenter中addObserver后,并无对这个对象进行引用计数加1操做,因此它只是保存了地址。为了验证这个操做,咱们来作下代码的测试。post


一个测试类,用来注册通知:测试


@implementation MRCObjectui

- (id)initspa

{.net

if (self = [super init]) {线程

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];debug

}

return self;

}

- (void)test

{

NSLog(@"=================");

}

- (void)dealloc

{

[super dealloc];

}

@end


这个类很简单,就是在初始化的时候,给他注册一个通知。可是在销毁的时候不进行remove操做。咱们在VC中建立这个对象后,而后销毁,最后发送这个通知:


- (void)viewDidLoad {

[super viewDidLoad];

MRCObject *obj = [[MRCObject alloc] init];

[obj release];

[[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];

}


在进入这个vc后,咱们发现挂了。。而打印出的信息是:


2015-01-19 22:49:06.655 测试[1158:286268] *** -[MRCObject test]: message sent to deallocated instance 0x17000e5b0


咱们能够发现,向野指针对象发送了消息,因此挂掉了。从这点来看,苹果实现也基本差很少是这样的,只保存了个对象的地址,并无在销毁的时候置为nil。


这点就能够证实,addObserver后,必需要有remove操做。


如今咱们在UIViewController中注册通知,不移除,看看会不会挂掉。


- (void)viewDidLoad {

[super viewDidLoad];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];

}


首先用navigationController进入到这个页面,而后pop出去。最后点击发送通知的按钮事件:


- (void)didButtonClicked:(id)sender

{

[[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];

}


不管你怎么点击这个按钮,他就是不挂!这下,是否是很郁闷了?咱们能够找找看,你代码里面没有remove操做,可是NSNotificationCenter那边已经移除了,否则确定会出现上面野指针的问题。看来看去,也只能说明是UIViewController本身销毁的时候帮咱们暗地里移除了。


那咱们如何证实呢?因为咱们看不到源码,因此也不知道有没有调用。这个时候,咱们能够从这个通知中心下手!!!怎么下手呢?我只要证实UIViewController在销毁的时候调用了remove方法,就能够证实咱们的猜测是对的了!这个时候,就须要用到咱们强大的类别这个特性了。咱们为NSNotificationCenter添加个类别,重写他的- (void)removeObserver:(id)observer方法:


- (void)removeObserver:(id)observer

{

NSLog(@"====%@ remove===", [observer class]);

}


这样在咱们VC中导入这个类别,而后pop出来,看看发生了什么!


2015-01-19 22:59:00.580 测试[1181:288728] ====TestViewController remove===


怎么样?是否是能够证实系统的UIViewController在销毁的时候调用了这个方法。(不建议你们在开发的时候用类别的方式覆盖原有的方法,因为类别方法具备更高的优先权,因此有可能影响到其余地方。这里只是调试用)。


以上也提醒咱们,在你不是销毁的时候,千万不要直接调用[[NSNotificationCenter defaultCenter] removeObserver:self]; 这个方法,由于你有可能移除了系统注册的通知。


正确姿式之注意重复addObserver


在咱们开发中,咱们常常能够看到这样的代码:


- (void)viewWillAppear:(BOOL)animated

{

[super viewWillAppear:animated];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];

}

- (void)viewWillDisappear:(BOOL)animated

{

[super viewWillDisappear:animated];

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"test" object:nil];

}


就是在页面出现的时候注册通知,页面消失时移除通知。你这边可要注意了,必定要成双成对出现,若是你只在viewWillAppear 中 addObserver没有在viewWillDisappear 中 removeObserver那么当消息发生的时候,你的方法会被调用屡次,这点必须牢记在心。


正确姿式之多线程通知


首先看下苹果的官方说明:


Regular notification centers deliver notifications on the thread in which the notification was posted. Distributed notification centers deliver notifications on the main thread. At times, you may require notifications to be delivered on a particular thread that is determined by you instead of the notification center. For example, if an object running in a background thread is listening for notifications from the user interface, such as a window closing, you would like to receive the notifications in the background thread instead of the main thread. In these cases, you must capture the notifications as they are delivered on the default thread and redirect them to the appropriate thread.


意思很简单,NSNotificationCenter消息的接受线程是基于发送消息的线程的。也就是同步的,所以,有时候,你发送的消息可能不在主线程,而你们都知道操做UI必须在主线程,否则会出现不响应的状况。因此,在你收到消息通知的时候,注意选择你要执行的线程。下面看个示例代码


//接受消息通知的回调

- (void)test

{

if ([[NSThread currentThread] isMainThread]) {

NSLog(@"main");

} else {

NSLog(@"not main");

}

dispatch_async(dispatch_get_main_queue(), ^{

//do your UI

});

}

//发送消息的线程

- (void)sendNotification

{

dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(defaultQueue, ^{

[[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];

});

}


总结


通知日常使用的知识点差很少就这么多。但愿对你们有帮助。最后,代码必定要养成良好的习惯,该移除的仍是要移除。

相关文章
相关标签/搜索