NSNotificationCenter removeObserver:在dealloc和线程安全中

时间:2012-12-17 08:25:54

标签: objective-c memory-management automatic-ref-counting nsnotificationcenter

我正在使用ARC而我正在观察者的[[NSNotificationCenter defaultCenter] removeObserver:someObserver];中呼叫dealloc

来自NSNotificationCenter Class Reference

  

请务必先调用此方法(或removeObserver:name:object :)   notificationObserver或在中指定的任何对象   addObserver:selector:name:object:is deallocated。

NSNotificationCenter不保留观察者。

Q1:NSNotificationCenter线程安全吗?

如果正在取消分配观察者(并从通知中心移除观察者),另一个线程同时发布通知。

我遇到随机崩溃,我怀疑是这种情况。

Q2:这种情况可能吗?

问题3:它是否会导致EXC_BAD_ACCESS

问题4:那么,在观察者[[NSNotificationCenter defaultCenter] removeObserver:someObserver];中呼叫dealloc是否安全?

问题5:如果不安全,我应该在哪里拨打removeObserver:

3 个答案:

答案 0 :(得分:11)

我自己偶然发现了这个问题:我在发送过程中发出了一个通知(总是发生在主线程中),而对象正处于从后台线程释放的过程中。我通过在主线程中执行removeObserver并等待:

来修复它
- (void)removeNotificationCenterObserver
{
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    [notificationCenter removeObserver:self];
}

- (void)dealloc
{
    [self performSelectorOnMainThread:@selector(removeNotificationCenterObserver) withObject:self waitUntilDone:YES];
}

这将等到当前运行循环周期结束并在下一个运行循环周期开始时执行此消息。这可确保任何仍在运行的功能都将完成。

答案 1 :(得分:2)

是的,NSNotificationCenter不会保留观察者,但它的调度表中仍然有一个指向它的指针。

Q1:引用Apple文档

  

常规通知中心会在发布通知的主题上发送通知。分布式通知中心在主线程上提供通知。有时,您可能需要在由您而不是通知中心确定的特定线程上发送通知。例如,如果在后台线程中运行的对象正在侦听来自用户界面的通知(例如关闭窗口),则您希望在后台线程而不是主线程中接收通知。在这些情况下,您必须捕获在默认线程上传递的通知,并将它们重定向到相应的线程。

Q2,3:是的。

Q4,5:除非你偶然发现循环参考,否则AFAIK是安全的。 我通常在-viewWillAppear: / -viewWillDisappear:中添加/删除UIViewControllers,为-init / dealloc添加/删除其他类。

答案 2 :(得分:2)

我想知道同样的事情,我找不到记录。这就是我的想法。

removeObserver:以您希望的方式不是线程安全的。

考虑以下情况。在线程A上执行代码时释放对观察者的最后一个引用。线程A将调用观察者的dealloc方法。同时,观察对象从线程B执行[NSNotificcationCenter postNotificationName:object:]。这导致不可避免的竞争条件。也就是说,当您的对象在其dealloc方法中时,通知将在飞行中

- (void)init {
    ...
    [[NSNotificcationCenter defaultCenter] addObserver:self
                                              selector:@selector(callback:)
                                                  name:@"whatever"
                                                object:nil];
    ...
}

- (void)dealloc {

    // If the observed object posts the notification on thread B while 
    // thread A is here, there's a race! At best, thread B will be
    // in callback: while thread A is here in dealloc. That's probably
    // not what you expect. The worst case is that thread B won't make
    // make it to callback: until after thread A completes the dealloc
    // and the memory has been freed. Likely crash!

    [[NSNotificationCenter defaultCenter] removeObserver:self];

    // If the observed object does the post when thread A is here,
    // you'll be fine since the observation has been removed.
}

对于仅观察其他主要线程对象的主线程对象,这不是问题,因为根据定义,您无法进入我所描述的线程A和B场景。

对于多线程案例,保证你避免这个问题的唯一方法是确保在观察者的引用次数达到0之前观察停止。如果其他人对观察者的生命周期负责(即你有任何termclose方法),这很容易。如果没有,我不知道解决方案。