ARC,块中的ivars和通过Captured Self引用循环问题

26

我在一个纯iOS5 / ARC环境中工作,因此可以根据需要使用__weak引用。在许多情况下,尤其是移动视图的动画块(这些视图是视图控制器类的属性)中,我会在块中引用实例变量。

我的问题:

在最简单的ivar在block中的用法中,我是否会创建引用循环?每次编写操纵包含对象的实例变量的块时,我是否需要使用__weak self / strong self技术?

我一直在重新观看2011年WWDC Session#322 (Objective-C Advancements in Depth),以理解有关25:03时间索引处开始的有关“通过捕获的Self引用循环”的3分钟片段的微妙之处。对我来说,这意味着在块中使用ivar应该像在那个片段中描述的那样,以弱self /强self设置进行保护。

下面是视图控制器上的示例方法,典型的动画方法如下。

在openIris块中,像我这样引用ivar“_topView”和“_bottomView”是否有误?

我应该在块之前始终设置__weak引用,然后在块内设置强引用,以了解先前设置的弱引用,然后通过该强引用在块内访问ivars吗?

从WWDC会话中,我了解到在块中引用ivar实际上是创建对这些ivar所依附的隐含self的引用。

对我来说,这意味着没有任何简单或微不足道的情况是正确的,在没有进行弱/强跳舞以确保没有循环的情况下访问块中的ivar。或者我是否把一个不适用于简单情况(例如我的示例)的角落案例读得太多了?

- (void)openIrisAnimated:(BOOL)animated
{
    if (_isIrisOpened) {
        NSLog(@"Asked to open an already open iris.");
        return; // Bail
    }

    // Put the common work into a block.
    // Note: “_topView” and “_bottomView” are the backing ivars of 
    // properties “topView” and “bottomView”
    void (^openIris)() = ^{
        _topView.frame     = CGRectMake(....);        
        _bottomView.frame  = CGRectMake(....);
    };

    // Now do the actual opening of the iris, whether animated or not:
    if (animated) {
        [UIView animateWithDuration:0.70f 
                         animations:^{
                             openIris();
                         }];
    }
    else {
        openIris();
    }

    _irisOpened = YES; // Because we have now just opened it
}

以下是我按照第322次会话的指导重新编写openIris块的方法,但我只想知道是否所有类似的块都需要进行弱/强引用操作以确保正确性和稳定性:

__weak MyClass *weakSelf = self;


void (^openIris)() = ^{
     MyClass *strongSelf = weakSelf;

     if (strongSelf) {        
        strongSelf.topView.frame     = CGRectMake(....);
        strongSelf.bottomView.frame  = CGRectMake(....);
     }
};

这么做真的有必要吗?

2个回答

22

只有当self继续持有块(或self拥有的某些东西)的引用时,才会出现循环。如果不是这样,那么您可以放心使用,因为块的生存期不由其保留的self所决定。

因此,在您的特定示例中,似乎没有问题。动画块不需要参与弱/强self交互。


谢谢Joshua。所以,块已经创建了。它引用了托管对象的实例变量,因此对self(例如视图控制器)有一个隐式强引用。块超出范围,因此被释放。块对self的强引用不会导致块逃脱释放。块能够存活的唯一原因是如果其他东西保留了对它的引用。仅仅因为块在被释放时包含强引用并不是它被保持活动的理由。 - idStar
块只是对象,它们并不具有任何神奇的特性。对象不会因为其实例数据而保持活动状态(除非存在循环引用)。 - David Dunham

4
值得担心的情况类似于addObserverForName:object:queue:usingBlock:。文档中说,“该块被通知中心复制。”在ARC下,这个“复制”词是一个红旗;现在你需要采取措施,以便你(调用者)不会泄漏。
编辑:此外,有时ARC本身会警告您。 -[UIPageViewController setViewControllers:direction:animated:completion:]的完成块就是一个例子。我从来没有想到在这里使用self可能会导致保留周期,但是ARC警告说会这样,所以我为了谨慎起见进行了弱-强交替。

这可能是ARC谨慎的表现:您正在将一个保留self的块传递给以“set”开头的方法。另一方面,-addObserverForName:object:queue:usingBlock:则会引发微妙的错误:您还需要持有返回值,以便稍后将其传递给-removeObserver: - tc.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接