NSNotificationCenter和安全的多线程 [英] NSNotificationCenter and safe multithreading

查看:111
本文介绍了NSNotificationCenter和安全的多线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

鉴于即使在进行方法调用时也可能会释放对象(

Given that objects may be deallocated even while a method invocation is in progress (link)*, is it safe for an object to register for and receive notifications that will be delivered on a thread that is different from the one on which it expects to be deallocated?

作为参考,文档指出

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

In a multithreaded application, notifications are always delivered in the thread in which the notification was posted, which may not be the same thread in which an observer registered itself.

NSNotificationCenter并不严格引用已注册以接收通知的对象.

Also important is the fact that NSNotificationCenter does not keep a strong reference to objects that are registered to receive notifications.

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

Here's an example that might make the situation more concrete:

- (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:返回之前,对该对象的最后一个强引用(在我看到的代码中)已损坏.

An object with this implementation receives SomeNotification on thread X. Before -handleNotification: returns, the last strong reference to the object (in the code I can see) is broken.

  1. 我是否正确地认为:

  1. Am I correct in thinking that:

a.如果NSNotificationCenter在调用-handleNotification:之前强引用了该对象,则直到-handleNotification:返回并且

a. If NSNotificationCenter takes a strong reference to the object before calling -handleNotification: on it, then the object will not be deallocated until after -handleNotification: returns, and

b.如果NSNotificationCenter在调用-handleNotification:之前未对对象进行强引用,则可以在-handleNotification:返回

b. if NSNotificationCenter does not take a strong reference to the object before calling -handleNotification: on it, then the object may be deallocated before -handleNotification: returns

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

Which way (a or b) does it work? I have not found this topic covered in the documentation yet, but it seems somewhat important to using NSNotificationCenter safely in a multithreaded environment.

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

UPDATE: The answer in the aforementioned link was updated indicating that "ARC retains and releases around an invocation on a weak reference". This means that an object should not be deallocated while a method invocation is in progress.

推荐答案

我始终建议,如果您看到通知在除main之外的其他线程上飞来飞去,并且您看到在后台发生了释放,则您的线程可能是太复杂. ObjC不是线程友好的语言.大多数线程工作应以队列上的短期块的形式进行.他们可以轻松地将通知发布回主线程,但不应经常使用通知.

I always recommend that if you're seeing notifications flying around on threads other than main, and you're seeing deallocations happen in the background, your threading may be too complicated. ObjC is not a thread-happy language. Most threading work should be in the form of short-lived blocks on queues. They can post notifications back to the main thread easily, but shouldn't be consuming notifications often.

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

That said, the best way today to manage multi-thread notifications is addObserverForName:object:queue:usingBlock:. This allows you much greater control over lifetimes. The pattern should look something like this:

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

请注意使用弱自我/强自我.我们避免使用弱势自身的保留循环.当我们回来时,我们会首先参考.如果我们仍然存在,那么我们将锁定该块的其余部分(因此我们无法在handleNotification:中取消分配).如果我们不存在,则该通知将被丢弃. (请注意,弱引用在调用dealloc之前会被有效地清零.请参见 objc_loadWeak .)我在这里使用mainQueue,但是您当然可以使用其他队列.

Note the use of weakself/strongself. We're avoiding a retain loop using weakself. When we come back, we take a strong reference first. If we still exist, then we're locked-in for the rest of the block (so we can't dealloc in handleNotification:). If we don't exist, then the notification is discarded. (Note that weak references are effectively zeroed before calling dealloc. See objc_loadWeak.) I'm using mainQueue here, but you could certainly use another queue.

在旧时代"(10.6之前的版本)中,我通过控制对象的生存期来解决此问题.基本上,我设计为使短命对象不监听可能来自其他线程的通知.这比听起来容易得多,因为在10.6之前的代码中,线程可以非常少地使用(而IMO,仍应保持在较低水平). NSNotificationCenter是为那个低线程世界设计的.

In the "old days" (pre-10.6), I designed around this problem by controlling object lifetimes. Basically, I designed such that short-lived objects didn't listen to notifications that might come from other threads. This was much easier than it sounds, because in pre-10.6 code, threading can be kept quite rare (and IMO, still should be kept to a low level). NSNotificationCenter was designed for that low-thread world.

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

Also note that unlike -[NSNotificationCenter addObserver:selector:name:object:], -[NSNotificationCenter addObserverForName:object:queue:usingBlock:] returns an opaque object that acts as the observer. You must keep track of this object so that you can remove the observer later on.

这篇关于NSNotificationCenter和安全的多线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆