8

鉴于即使在方法调用正在进行时对象也可能被释放(链接 *,对象注册并接收将在与其期望的线程不同的线程上传递的通知是否安全解除分配?

作为参考,文档指出

在多线程应用程序中,通知总是在发布通知的线程中传递,这可能与观察者注册自己的线程不同。

同样重要的是 NSNotificationCenter 不会保留对已注册以接收通知的对象的强引用。

这是一个可能使情况更加具体的示例:

- (id)init {
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:SomeNotification object:nil];
    }
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)handleNotification:(NSNotification *)notification {
    // do something
}

具有此实现的对象在线程 X 上接收 SomeNotification。在 -handleNotification: 返回之前,对该对象的最后一个强引用(在我可以看到的代码中)被破坏。

  1. 我的想法是否正确:

    一种。如果 NSNotificationCenter 在调用 -handleNotification: 之前对该对象进行了强引用,则在 -handleNotification: 返回之前不会释放该对象,并且

    湾。如果 NSNotificationCenter 在调用 -handleNotification: 之前没有对该对象进行强引用,则该对象可能在 -handleNotification: 返回之前被释放

  2. 它以哪种方式(a 或 b)起作用?我还没有在文档中找到这个主题,但是在多线程环境中安全地使用 NSNotificationCenter 似乎有些重要。

更新:上述链接中的答案已更新,表明“ARC 围绕弱引用的调用保留和释放”。这意味着在方法调用正在进行时不应释放对象。

4

2 回答 2

8

我始终建议,如果您看到通知在除主线程之外的线程上飞来飞去,并且您看到在后台发生释放分配,那么您的线程可能太复杂了。ObjC 不是线程快乐的语言。大多数线程工作应该以队列上的短期块的形式出现。他们可以轻松地将通知发布回主线程,但不应该经常使用通知。

也就是说,当今管理多线程通知的最佳方式是addObserverForName:object:queue:usingBlock:. 这使您可以更好地控制生命周期。该模式应如下所示:

__weak id weakself = self;
id notificationObserver = [[NSNotificationCenter defaultCenter]
 addObserverForName:...
 object:...
 queue:[NSOperationQueue mainQueue]
 usingBlock:^(NSNotification *note){
   id strongself = weakself;
   if (strongself) {
     [strongself handleNotification:note];
   }
 }];

注意使用weakself/strongself。我们正在避免使用weakself 的保留循环。当我们回来时,我们首先采取强参考。如果我们仍然存在,那么我们将被锁定在块的其余部分(所以我们不能在 中释放handleNotification:)。如果我们不存在,则通知被丢弃。(请注意,弱引用在调用之前有效地归零dealloc。请参阅objc_loadWeak。)我在mainQueue这里使用,但您当然可以使用另一个队列。

在“旧时代”(10.6 之前),我通过控制对象生命周期来围绕这个问题进行设计。基本上,我的设计使得短期对象不会监听可能来自其他线程的通知。这比听起来要容易得多,因为在 10.6 之前的代码中,线程可以保持非常少见(而 IMO,仍应保持在低水平)。NSNotificationCenter是为那个低线程世界设计的。

另请注意,与-[NSNotificationCenter addObserver:selector:name:object:],不同的是,它-[NSNotificationCenter addObserverForName:object:queue:usingBlock:]返回一个充当观察者的不透明对象。您必须跟踪此对象,以便以后可以删除观察者。

于 2012-12-06T15:03:26.913 回答
0

NSNotificationCenter 不对对象进行强引用,因此必须在释放前移除观察者。启用 ARC 时,如果正在调用 handleNotification,则不会释放观察者,因为调用 handleNotification 会增加其保留计数。如果在通知发布之前观察者被释放,NSNotificationCenter 将在您编写 dealloc 方法时将其从观察者中删除,以便不会调用 handleNotification。NSNotificationCenter 在发布通知时同步调用通知处理程序。

于 2016-08-30T08:54:06.127 回答