关于Apple的Block Docs的澄清?

我正在解决块/ ARC的一些保留周期问题,我试图了解细微差别。 任何指导表示赞赏。

Apple关于“块和变量”的文档(http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html)说明如下:

如果在方法的实现中使用块,则对象实例变量的内存管理规则更加微妙:

如果通过引用访问实例变量,则保留self; 如果按值访问实例变量,则保留变量。 以下示例说明了两种不同的情况:

dispatch_async(queue, ^{ // instanceVariable is used by reference, self is retained doSomethingWithObject(instanceVariable); }); id localVariable = instanceVariable; dispatch_async(queue, ^{ // localVariable is used by value, localVariable is retained (not self) doSomethingWithObject(localVariable); }); 

我发现这个解释令人困惑。

  1. 这是否适当地使用了“按价值”/“按参考”的术语? 假设这些变量属于同一类型(id),它们之间的区别似乎是它们的范围。
  2. 我没有看到“参考”示例中的自我是如何被引用的? 如果正在使用访问器方法(例如 – 下面),我可以看到自己被保留。

    doSomethingWithObject(self.instanceVariable);

  3. 您是否有任何关于何时可能想要以某种方式做事的指导?

  4. 如果传统的智慧是利用“按价值”变量,似乎这将导致额外的变量声明的额外代码?
  5. 在嵌套块正在发挥作用的情况下,似乎可以更加可维护,以避免在彼此内部声明块,因为最终可能最终会出现无意中保留的对象的大杂烩?

  1. 这是否适当地使用了“按价值”/“按参考”的术语? 它至少类似于典型用途。 将值复制到局部变量就像将值复制到堆栈中一样; 使用ivar就像将指针传递给存储在其他地方的值。

  2. 我没有看到“参考”示例中的自我是如何被引用的? 在方法中使用实例变量时,隐含对self的引用。 有些人实际上写了self->foo而不是foo来访问ivar只是为了提醒自己foo是一个ivar。 我不建议这样做,但重点是fooself->foo意思相同。 如果您访问块内的ivar,将保留self以确保在块的持续时间内保留ivar。

  3. 您是否有任何关于何时可能想要以某种方式做事的指导? 在这里考虑通过引用/通过值区分来考虑是有用的。 正如AliSoftware所解释的那样,在创建块时会保留局部变量,就像在调用函数时复制通过值传递的参数一样。 通过self访问ivar,因为通过引用传递的参数是通过指针访问的,因此在您实际使用它之前不会确定其值。

  4. 看起来这会导致额外的变量声明的额外代码? 块现在已经成为语言的一个特征,我没有注意到这是一个问题。 更常见的是,您需要相反的行为:本地声明的变量,您可以在块(或多个块)中进行修改。 __block存储类型使这成为可能。

  5. 似乎可以更加可维护,以避免在彼此内部声明块,因为最终可能最终会出现无意中保留的物体的大杂烩? 让一个或多个块在需要时保留一个对象没有任何问题 – 只要使用它的块终止就会释放该对象。 这完全符合通常的Objective-c手动内存管理理念,每个对象只关心平衡自己的保留。 避免多层嵌套块的更好理由是,这种代码可能比它需要的更难理解。

可以想象使用instanceVariable作为编写self->instanceVariable的等价物。 根据定义,实例变量“附加”到self对象,并且在自身对象存在时存在。

使用instanceVariable (或self->instanceVariable )意味着你从self的地址开始,并要求一个实例变量(即从self对象原始地址偏移一些字节)。

使用localVariable本身就是一个变量,它不依赖于self,也不是相对于另一个对象的地址。

由于块在创建变量时会捕获变量,因此当您意味着“执行块时,我希望在执行时获取实例变量的值”时,您通常更喜欢使用实例变量,因为您会问self当时实例变量值的对象(与调用[self someIVarAccessorMethod]方式完全相同)。 但是请注意不要创建一些保留周期。

另一方面,如果使用localVariable ,则在创建块时将捕获局部变量(而不是self ),因此即使局部变量在块创建后发生更改,旧值也将在块内使用。

 // Imagine instanceVariable being an ivar of type NSString // And property being a @property of type NSString too instanceVariable = @"ivar-before"; self.property = @"prop-before"; NSString* localVariable = @"locvar-before"; // When creating the block, self will be retained both because the block uses instanceVariable and self.property // And localVariable will be retained too as it is used directly dispatch_block_t block = ^{ NSLog(@"instance variable = %@", instanceVariable); NSLog(@"property = %@", self.property); NSLog(@"local variable = %@", localVariable); }; // Modify some values after the block creation but before execution instanceVariable = @"ivar-after"; self.property = @"prop-after"; localVariable = @"locvar-after"; // Execute the block block(); 

在该示例中,输出将显示通过self对象访问instanceVariableself.property ,因此self被保留,但是在块的代码中查询了instanceVariableself.property的值,并且它们将在当时返回它们的值执行,分别是"ivar-after""prop-after" 。 另一方面, localVariable在创建块时保留,并且其值在此时被const复制,因此最后一个NSLog将显示"locvar-before"

当您在块的代码中使用实例变量或属性或在self本身上调用方法时,将保留self。 直接在块的代码中使用局部变量时,会保留它们。

注意:我建议你观看WWDC’11和WWDC’12video,讨论这个主题,它们真的很有启发性。