NSNotificationCenter和安全的多线程

And*_*ger 8 multithreading objective-c nsnotificationcenter

假定即使在方法调用正在进行(链接)*时也可以释放对象,对象是否可以安全地注册和接收将在与其预期的线程不同的线程上传递的通知.释放?

作为参考,文档说明了这一点

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

同样重要的是,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
}
Run Code Online (Sandbox Code Playgroud)

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

  1. 我是否正确地认为:

    一个.如果NSNotificationCenter在调用-handleNotification之前对该对象进行强引用,那么在-handleNotification:return之后,该对象将不会被释放,并且

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

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

更新:上述链接中的答案已更新,表明"ARC保留并释放弱引用的调用".这意味着在方法调用过程中不应释放对象.

Rob*_*ier 8

我总是建议,如果你看到除了main之外的线程上的通知,并且你看到在后台发生解除分配,你的线程可能太复杂了.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];
   }
 }];
Run Code Online (Sandbox Code Playgroud)

注意使用weakself/strongself.我们正在避免使用弱自己的保留循环.当我们回来时,我们首先采取强有力的参考.如果我们仍然存在,那么我们就会锁定其余的块(所以我们不能解除锁定handleNotification:).如果我们不存在,则丢弃通知.(请注意,在调用之前,弱引用实际上已归零dealloc.请参阅objc_loadWeak.)我在mainQueue这里使用,但您当然可以使用另一个队列.

在"过去的日子"(10.6之前)中,我通过控制对象的生命周期来设计这个问题.基本上,我设计的是短期对象不会监听可能来自其他线程的通知.这比听起来容易得多,因为在10.6之前的代码中,线程可以保持非常罕见(IMO仍然应该保持在低水平).NSNotificationCenter是为那个低线程世界而设计的.

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