【问题标题】:How to deal with block referencing object that deallocates如何处理释放的块引用对象
【发布时间】:2014-03-15 07:25:47
【问题描述】:

考虑一个执行此操作的UIViewController 子类(使用ARC):

- (void)viewDidLoad {
    __weak id wself = self;
    dispatch_async(backgroundQueue, ^{
        longRunningOperation();
        dispatch_async(mainQueue, ^{
            [wself updateView];
        });
    });
}

问题

  1. 如果视图控制器在块仍在执行时解除分配会发生什么?
  2. 如果视图控制器在运行时解除分配,如何让块立即停止执行?

【问题讨论】:

  • 反对者:请发表评论。

标签: objective-c uiviewcontroller automatic-ref-counting objective-c-blocks


【解决方案1】:

如果您需要支持取消,那么NSOperation/NSOperationQueue 更容易使用。

如果对象被解除分配,那么在大多数情况下它应该不会导致任何问题,因为在 nil 上调用方法是无用的。但是,如果您使用任何直接 ivar 访问 (self->myIvar),那么您可能会遇到崩溃,因为您将取消对 nil 的引用。解决此问题的一种方法是在对对象执行任何操作之前获得强引用:

- (void)viewDidLoad
{
  [super viewDidLoad];

  __weak __typeof(self) weakSelf = self;
  dispatch_async(backgroundQueue, ^{
    longRunningOperation();

    dispatch_async(mainQueue, ^{
      __typeof(weakSelf) strongSelf = weakSelf;
      strongSelf->myIvar;
    });

  });
}

【讨论】:

    【解决方案2】:

    检查this SO question,那里有一个很好的答案,其中详细描述了 GCD 缺乏取消功能。

    除此之外,如果您尝试执行此代码并在块执行时检查 wself 和 self 的值(分别在检查 wself 时注释掉 self),您会看到 wself变为nil 而self 仍然具有价值.. 所以我认为你避免在这里直接引用self 是正确的。如果您在同一块中同时检查wself 和self,您将wself 仍然有一个值。因此,如果您直接在该块中引用它,则该块基本上能够使self 存活更长时间。因此,使用wself 将确保您不会无意中使事物保持比预期更长的时间,因此选择器实际上不会做任何事情。

    因此,您可以检查wself 在执行时是否已在块中变为nil,或者您可以仅依靠Objective-C 在将消息传递给nil 时安静地什么都不做的能力(只要它不是一个无法识别的选择器)。

    【讨论】:

      【解决方案3】:

      在此代码中使用 wself 可能允许在 longRunningOperation 仍在执行时释放视图控制器。我认为在这种情况下不需要传递弱自我,因为没有机会进行涉及块的循环引用。

      就中断执行而言,最好的办法是在 ViewDidDisappear 中设置一个标志,表明该视图不再可见,并在执行 updateView 之前检查该标志。

      【讨论】:

      • 如果用户切换到另一个应用程序或应用程序模态呈现另一个视图控制器,操作将取消。
      【解决方案4】:

      如果您需要取消一些操作,最好使用 NSOperation 和 NSOperationQueue 进行编程。这将允许您取消应该与视图一起释放的操作

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-31
        • 1970-01-01
        • 1970-01-01
        • 2011-09-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-22
        相关资源
        最近更新 更多