关于Apple的Block Docs的澄清?

时间:2012-09-05 18:33:56

标签: ios objective-c-blocks

我正在处理块/ 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. 在嵌套块正在发挥作用的情况下,似乎可以更加可维护,以避免在彼此内部声明块,因为最终可能最终会出现无意识保留对象的大杂烩?

2 个答案:

答案 0 :(得分:4)

考虑使用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();

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

当您在块的代码中使用实例变量或属性或在self本身上调用方法时,将保留

"locvar-before"。在块的代码中直接使用局部变量时,会保留局部变量。

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

答案 1 :(得分:3)

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

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

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

  4. 这似乎会为其他变量声明带来许多额外的代码吗? 块已成为该语言的一小部分功能现在,我没有注意到这是一个问题。更常见的是,您需要相反的行为:本地声明的变量,您可以在块(或多个块)中进行修改。 __block存储类型使这成为可能。

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