当两个对象相互存储强引用时,就会发生保留循环。最简单的情况是对象a存储对对象的强引用b并b执行相反的操作 [1]。保留周期是 Objective-C 中的一个问题,因为它们使 ARC 相信这些对象总是在使用中,即使这些对象没有从其他任何地方引用。
让我们回顾一些例子。你有一个对象z,它分配aand b,使用它们,然后处理它们。如果a并且b首先在它们之间创建了一个保留循环,a并且b不会被释放。如果您多次这样做,您将严重泄漏内存。
保留循环的另一个真实示例是如果a分配并强引用一个b对象,但您还存储了一个强引用 from bto a(对象图中的许多较小的对象可能需要访问它们的 parent)。
在这些情况下,最常见的解决方案是确保包含的对象仅对其包含对象具有弱引用,并确保兄弟对象之间不包含对彼此的强引用。
另一种解决方案(通常不太优雅,但在某些情况下可能合适)可能是使用某种自定义cleanup方法,a因为它不引用b. 因此在被调用b时会被释放cleanup(如果b在其他地方没有强烈引用)。这很麻烦,因为您不能从a's执行此操作dealloc(如果存在保留周期,它永远不会被调用)并且因为您必须记住cleanup在适当的时间调用。
- 请注意,保留循环也是可传递的(例如,对象
a强烈引用b哪个强烈引用c哪个强烈引用a)。
尽管如此:块的内存管理很难理解。
您的第一个示例可以创建一个临时保留周期(并且仅当您的self对象存储对 的强引用时someObject)。当块完成执行并被释放时,这个临时保留周期就会消失。
在执行期间,将再次存储对、和 的self引用。但同样,它只是暂时的,因为该块不会永久存储在任何地方(除非实现这样做,但这对于完成块并不常见)。someObjectsomeObjectblockblockself[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 指出它。