在从UIViewController调用的非保留完成中引用self时,weakSelf / strongSelf舞蹈真的是必要的吗?

时间:2014-01-14 12:48:43

标签: ios objective-c uiviewcontroller objective-c-blocks retain-cycle

假设我在UIViewController子类中有以下方法:

- (void)makeAsyncNetworkCall
{
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        dispatch_async(dispatch_get_main_queue(), ^{
                [self.activityIndicatorView stopAnimating];
            }
        });
    }];
}

我知道块中self的引用导致块保留UIViewController实例。只要performAsyncNetworkCallWithCompletion没有将块存储在NetworkService的属性(或ivar)中,我是否认为没有保留周期?

我意识到上面的这个结构将导致UIViewController被保留,直到performAsyncNetworkCallWithCompletion完成,即使它是由系统早先发布的。但它可能(甚至可能?)系统将取消分配我的UIViewController after the changes to the way iOS 6 manages a UIViewController's backing CALayer memory)?

如果有理由我必须做“弱自我/强自我舞蹈”,它会是这样的:

- (void)makeAsyncNetworkCall
{
    __weak typeof(self) weakSelf = self;
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }
        dispatch_async(dispatch_get_main_queue(), ^{
                [strongSelf.activityIndicatorView stopAnimating];
            }
        });
    }];
}

但是我觉得这很难看,并且如果没有必要的话就想避免它。

4 个答案:

答案 0 :(得分:29)

由于我相信您已正确诊断,因此在此方案中使用self不一定会导致强大的参考周期。但是这将在网络操作完成时保留视图控制器,在这种情况下(如在大多数情况下),没有必要。因此,可能没有必要使用weakSelf,但这样做可能是谨慎的。它最大限度地减少了意外强引用周期的可能性,并导致更有效地使用内存(一旦视图控制器被关闭就释放与视图控制器关联的内存,而不是在网络操作完成之后不必要地保留视图控制器)

不需要strongSelf构造。你可以:

- (void)makeAsyncNetworkCall
{
    __weak typeof(self) weakSelf = self;
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf.activityIndicatorView stopAnimating];
        });
    }];
}

您只需要weakSelf / strongSelf组合,其中有一个强大的参考(例如,您正在取消引用ivars)或者您需要担心竞争条件。这似乎不是这种情况。

答案 1 :(得分:6)

我认为问题在于networkService可能会对该块保持强引用。并且视图控制器可能具有对networkService的强引用。因此,VC-> NetworkService-> block-> VC的可能的循环可能存在。但是,在这种情况下,通常可以安全地假设块在运行后将被释放,在这种情况下循环被破坏。因此,在这种情况下,没有必要。

必要时,如果块未被释放。比方说,你没有一个在网络调用后运行一次的块,而是有一个用作回调的块。即networkService对象维护对该块的强引用,并将其用于所有回调。在这种情况下,块将具有对VC的强引用,这将创建一个强循环,因此首选弱引用。

答案 2 :(得分:3)

不,如果你的self.networkService不使用它作为块属性你应该没问题

答案 3 :(得分:0)

答案在这里并不那么简单。我同意@ Rob的回答,但需要补充说明:

  1. __weak被认为是一种安全的方式,因为它在释放时会使自己失去意义,这意味着如果在调用对象已经被释放时发生回调很久就会发生回调,由块引用,就像从堆栈中弹出UIViewController。增加取消任何操作的可能性仅仅是一个卫生问题,也可能是资源问题。例如,您也可以取消NSURLConnection,它不仅可以取消NSOperation,还可以在回调阻止的方法中异步取消正在执行的任何内容。

  2. 如果允许self保留块,那么如果UIViewController正在释放UINavigationController之类的调用者对象并且块仍然保留它,则故事可能会有点复杂回电话。在这种情况下,将执行回调块并假设某些数据将根据其结果进行更改。这甚至可能是想要的行为,但在大多数情况下并非如此。因此,在这种情况下取​​消操作可能更为重要,通过从UINavigationControllerDelegate作为关联对象或单例驻留在可变集合中的异步任务取消UINavigationController方法非常明智。 / p>

  3. 当然,保存下注是第一个选项,但仅限于您在解除调用者对象后不希望异步操作继续的情况。