我可以在-dealloc中调用[self retain]吗?或者,我如何确保主线程上发生dealloc?

时间:2011-04-24 07:46:16

标签: objective-c ios thread-safety dealloc objective-c-blocks

这是我的情况。这很复杂所以请耐心等待。

我有一个视图类,我们称之为MyView。它会创建一个加载指示器子视图,然后启动一个将加载数据的后台操作。它还会创建一个块,后台队列操作在完成后将在主队列中排队。该块通过添加另一个子视图UITextView来准备视图,其中包含已加载的数据。当然,要做到这一点,块必须引用视图。

因此后台操作会保留块,并且块保留视图。和我一起到目前为止?

有时在后台队列操作完成之前,会从其超级视图中删除MyView的实例。有时,在后台队列操作被完全清除之前,主要的队列操作(调用块)会被彻底清除。在这种情况下,MyView的实例可以在后台线程上进行-dealloc调用,因为对视图的最后一个引用属于该块,并且该块的最后一个引用属于后台操​​作

UIKit不喜欢从任何线程调用,而是主线程。在UITextView的情况下,显然甚至包括-dealloc次来电。在文本视图的EXC_BAD_ACCESS期间,我从称为“网络线程锁”的内容中获取-dealloc

我认为后台线程有时会有最后一个引用是合理的,我想在我的-dealloc实现中处理这个问题,如下所示:

- (void)dealloc {
    if ([NSOperationQueue currentQueue] == [NSOperationQueue mainQueue]) {
        // The usual -- dealloc subviews safely on the main thread
        self.myIvar = nil;
        [super dealloc];
    }
    else {
        // Not on the main thread, so keep the object alive
        // in spite of the dealloc call.
        [self retain];                   // explicit retain
        [[NSOperationQueue mainQueue]
         addOperationWithBlock:^{        // implicit retain at block creation
            [self release];              // explicit release
        }];                              // implicit release, dealloc called again, but on the main thread
    }
}

因此,当您在某个对象上调用-release时,NSObject中的实现会在保留计数达到零时调用-dealloc。这一切都发生了吗?换句话说,是否可以拨打-dealloc而不是来拨打super?我制作某种令人讨厌的僵尸物品还是这样吗?

如果这不行,那么确保在主线程上调用-dealloc的好方法是什么?

4 个答案:

答案 0 :(得分:5)

为什么不覆盖释放?

- (void)release
{
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
    } else {
        [super release];
    }
}

编辑:这是预ARC。请勿使用ARC。

答案 1 :(得分:1)

我见过这种方法:

- (void)dealloc {
  if (is on main thread) {
    [self _reallyDealloc];
  } else {
    [self performSelectorOnMainThread:@selector(_reallyDealloc)];
  }
}

- (void)_reallyDealloc {
  [ivar release];
  [super dealloc]
}

这仍然不理想(并在ARC中断)。最好的答案是保证最终版本发生在主线程上。

原谅伪代码。在iPhone上键入代码是次优的

答案 2 :(得分:1)

为什么不避免将视图传递给在后台线程上执行的块?您可以让控制器注册通知,并让阻止在主线程的通知中心发出通知:

NSNotification *notification = [NSNotificationnotificationWithName:@"data upated" object:nil];
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:notification waitUntilDone:NO];

答案 3 :(得分:1)

一旦调用dealloc,就太晚了。无论你做什么,该对象都会消失。如果问题是dealloc做了在后台线程上不安全的事情,那么dealloc可以将这些东西发送到主队列,当“self”早已消失时它们将被执行。

重写“保留”是完全变态的。让我们说当你在stack overflow.com上寻求建议时,你不应该做些什么。然后就是ARC,无论如何你都无法做到这一点。