【问题标题】:ARC, ivars in Blocks and Reference Cycles via Captured SelfARC,块中的 ivars 和通过捕获的自我的参考周期
【发布时间】:2011-11-04 15:19:47
【问题描述】:

我在纯 iOS5/ARC 环境中工作,所以我可以根据需要使用 __weak 引用。在许多情况下,我确实在块中引用 ivars,最值得注意的是,移动视图的动画块,例如,我的视图控制器类的属性。

我的问题:

在块中最简单的 ivars 使用中,我是在创建一个引用循环吗?我是否需要使用 __weak self / strong self 技术每次我编写一个操作包含对象的实例变量的块?

我一直在重新观看 2011 年 WWDC 会议 #322(Objective-C 深度改进),以了解从时间索引 25:03 开始​​的 3 分钟片段“通过捕获的自我进行参考循环”的细微差别。对我来说,这意味着块中任何 ivars 的使用都应该通过该部分中描述的弱自我/强自我设置来保护。

以下视图控制器上的示例方法是我所做的典型动画。

在openIris块中,像我一样引用ivars“_topView”和“_bottomView”是不是错了?

我是否应该始终在块之前设置对 self 的 __weak 引用,然后在块内对之前设置的弱引用设置强引用,然后通过块内的强引用访问 ivars?

从 WWDC 会议中,我了解到在块中引用 ivars 实际上是在创建对这些 ivars 所依赖的隐含自我的引用。

对我来说,这意味着确实没有任何简单或琐碎的情况可以在没有弱/强舞蹈以确保没有循环的情况下访问块中的 ivars 是正确的。或者我是否阅读了很多不适用于简单案例的极端案例,例如我的示例?

- (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
}

以下是我使用 Session #322 的指导重写 openIris 块的方法,但我只是想知道我的所有类似块是否都需要这种弱/强参考舞蹈来确保正确性和稳定性:

__weak MyClass *weakSelf = self;


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

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

这真的有必要吗?

【问题讨论】:

    标签: objective-c ios ios5 objective-c-blocks automatic-ref-counting


    【解决方案1】:

    这里只有一个循环,如果 self 然后继续持有对块的引用(或 self 拥有的东西)。如果不是,你最好去,因为块的生命周期不是由它保留的 self 决定的。

    因此,在您的特定示例中,您似乎很清楚。动画块不需要参与弱/强自舞。

    【讨论】:

    • 谢谢约书亚。因此,创建了块。它引用了宿主对象的 ivars,所以有一个对 self 的隐式强引用(比如视图控制器)。该块超出范围,因此被释放。块对 self 的强引用不会导致块逃脱释放。该块可以存在的唯一原因是其他东西是否保留了对它的引用。通过简单地超出范围,块包含的强引用就会被清除。只是 b/c 块包含强引用,因为它正在被释放,没有理由让它保持活动状态吗?
    • 块只是对象,它们没有什么神奇之处。对象永远不会因为它们自己的实例数据而保持活动状态(除了通过循环)。
    【解决方案2】:

    需要担心的情​​况类似于addObserverForName:object:queue:usingBlock:。文档说,“通知中心复制了该块。”在 ARC 下,“复制”这个词是一个危险信号;现在您需要采取措施,使您(调用者)不会泄漏。

    编辑:另外,有时 ARC 本身会提醒您。 -[UIPageViewController setViewControllers:direction:animated:completion:] 的完成块就是一个很好的例子。我永远不会怀疑在这里使用self 可能会导致一个保留循环,但 ARC 警告它会,所以我做了弱强之舞以防万一。

    【讨论】:

    • 这可能是 ARC 谨慎的原因:您将保留 self 的块传递给以“set”开头的方法。 -addObserverForName:object:queue:usingBlock:,OTOH,是对细微错误的邀请:您保留返回值,以便稍后将其传递给 -removeObserver:
    猜你喜欢
    • 2014-02-09
    • 2011-12-07
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多