带块的保留循环

The retain cycle with block

关于块的保留循环的问题。在 ARC 模型中。

比如说,一个名为 'vc' 的视图控制器实例,它持有对一个块的引用。在块中,vc 用于某些操作:

{

    //this is a piece of code snippet in vc
    self.vcBlock = {

        [self someAction];

    }

}

我知道这会导致保留周期,因为 vc 持有对块的强引用,而块也会持有对 vc 的强引用。

但是如何在块中引用 vc 的成员变量:

{

    //this is a piece of code snippet in vc
    self.vcBlock = {

        [self.obj someAction];

    }

}

这会导致保留循环吗?我认为这些引用的关系可以解释如下:

所以,我认为不存在循环引用,有什么问题吗?

属性 访问在编译时被转换为方法调用,所以在你的第二个例子中,它仍然是 self 被保留,并且你有完全相同的循环。

它确实存在循环引用,vcBlock holds a strong reference to self(the vc), 或者你可以把它当作

[[self obj] someAction];

如果我们像这样以另一种形式调用这个方法(如果 obj 是一个 ivar)

[_obj someAction];//in fact ,it's equivalent to [self->obj someAction]

因此块始终持有对 self 的强引用。

第二种情况,可以避免retain cycle:

{
    MyObject* obj = self.obj;
    self.vcBlock = ^{ [obj someAction]; };
}

表达式self.obj是在运行时计算的,这需要块保持self的值,所以你仍然有一个循环。

但是,如果您的图表是您想要的关系,那就是您的块具有对对象的引用,该对象在创建块时被 self.obj 引用 那么你可以简单地通过使用一个临时局部变量来实现。 IE。代码如下:

SomeType objRef = self.obj;
self.vcBlock = ^{ ... [objRef someAction]; ... };

这避免了循环,因为 self.obj 返回的 value(对象引用)首先被复制到本地 objRef,然后 value 成为块保存值的一部分(也就是它的 environmemt)。

在这种情况下,这是一种常见的方法,但请记住,如果 self.obj 的值在块执行之前发生变化,那么该变化将 不会被看到 按块 - 这似乎是您希望从图表中看到的。

另请注意,循环 本身 不是问题,您可以创建循环并在稍后打破它们而不会出现问题 - 实际上这并不少见。一个循环只有在导致未回收资源(如内存)时才是问题。

HTH