当两个对象相互存储强引用时,就会发生保留循环。最简单的情况是对象a
存储对对象的强引用b
并b
执行相反的操作 [1]。保留周期是 Objective-C 中的一个问题,因为它们使 ARC 相信这些对象总是在使用中,即使这些对象没有从其他任何地方引用。
让我们回顾一些例子。你有一个对象z
,它分配a
and b
,使用它们,然后处理它们。如果a
并且b
首先在它们之间创建了一个保留循环,a
并且b
不会被释放。如果您多次这样做,您将严重泄漏内存。
保留循环的另一个真实示例是如果a
分配并强引用一个b
对象,但您还存储了一个强引用 from b
to a
(对象图中的许多较小的对象可能需要访问它们的 parent)。
在这些情况下,最常见的解决方案是确保包含的对象仅对其包含对象具有弱引用,并确保兄弟对象之间不包含对彼此的强引用。
另一种解决方案(通常不太优雅,但在某些情况下可能合适)可能是使用某种自定义cleanup
方法,a
因为它不引用b
. 因此在被调用b
时会被释放cleanup
(如果b
在其他地方没有强烈引用)。这很麻烦,因为您不能从a
's执行此操作dealloc
(如果存在保留周期,它永远不会被调用)并且因为您必须记住cleanup
在适当的时间调用。
- 请注意,保留循环也是可传递的(例如,对象
a
强烈引用b
哪个强烈引用c
哪个强烈引用a
)。
尽管如此:块的内存管理很难理解。
您的第一个示例可以创建一个临时保留周期(并且仅当您的self
对象存储对 的强引用时someObject
)。当块完成执行并被释放时,这个临时保留周期就会消失。
在执行期间,将再次存储对、和 的self
引用。但同样,它只是暂时的,因为该块不会永久存储在任何地方(除非实现这样做,但这对于完成块并不常见)。someObject
someObject
block
block
self
[someObject successBlock:failure:]
因此,在您的第一个示例中,保留周期不是问题。
通常,只有在某个对象正在存储块而不是直接执行它时,块内的保留周期才是一个问题。那么很容易看出,self
强引用了block
,而block
强引用了self
。请注意,从块内部访问任何ivar会自动在该块中生成强引用self
。
确保包含的对象不强烈引用其容器的等效__weak SelfClass *weakSelf = self
项用于访问方法和 ivars(如果通过访问器访问 ivars 会更好,就像使用属性时一样)。您对块的引用self
将是弱的(它不是一个副本,它是一个弱引用)并且self
当它不再被强引用时将允许解除分配。
可以说,最好始终weakSelf
在所有块内部使用,无论是否存储,以防万一。我想知道为什么 Apple 没有将此作为默认行为。这样做通常不会对块代码造成任何有害的影响,即使实际上是不需要的。
__block
很少用在指向对象的变量上,因为 Objective-C 没有像这样强制对象的不变性。
如果你有一个指向对象的指针,你可以调用它的方法,这些方法可以修改它,有或没有__block
. __block
对基本类型(int、float 等)的变量更有用(仅?)。请参阅此处__block
了解与对象指针变量一起使用时会发生什么。您还可以__block
在Apple的Blocks Programming Topics中阅读更多信息。
编辑:修正了关于__block
对象指针使用的错误。感谢@KevinDiTraglia 指出它。