6

我正在使用 ARC,我正在调用[[NSNotificationCenter defaultCenter] removeObserver:someObserver];观察者的dealloc.

来自NSNotificationCenter 类参考

确保在 notifyObserver 或 addObserver:selector:name:object: 中指定的任何对象被释放之前调用此方法(或 removeObserver:name:object:)。

NSNotificationCenter 不保留观察者。

Q1:NSNotificationCenter线程安全吗?

如果观察者被释放(并从通知中心删除观察者),另一个线程同时发布通知。

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

Q2:这种情况可能吗?

Q3:它会导致EXC_BAD_ACCESS

Q4:那么,调用[[NSNotificationCenter defaultCenter] removeObserver:someObserver];观察员安全dealloc吗?

Q5:如果不安全,我应该在哪里打电话removeObserver:

4

3 回答 3

13

我自己只是偶然发现了这个问题:我在发送过程中收到了一个通知(这总是发生在主线程中),而对象正在从后台线程中释放。我通过简单地removeObserver在主线程中执行并等待来修复它:

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

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

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

于 2013-10-05T00:13:19.160 回答
2

我想知道同样的事情,但我找不到它的记录。这就是我认为正在发生的事情。

removeObserver:不是你想要的线程安全的。

考虑以下情况。在线程 A 上执行代码时释放对观察者的最后一个引用。线程 A 将调用观察者的dealloc方法。同时,被观察对象[NSNotificcationCenter postNotificationName:object:]从线程 B 执行 a。这导致了不可避免的竞争条件。也就是说,当您的对象在其方法中时,通知将在飞行中。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方法) , 这很简单。如果没有,我不知道解决方案。

于 2013-02-08T22:11:02.917 回答
2

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

Q1:引用 Apple 文档

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

Q2,3:是的。

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

于 2012-12-17T12:51:14.347 回答