我正在处理块/ 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);
});
我发现这个解释令人困惑。
我没有看到“参考”示例中的自我是如何被引用的?如果正在使用访问者方法(例如 - 下面),我可以看到自己被保留。
doSomethingWithObject(self.instanceVariable);
关于何时可能想要以某种方式做事,你有什么指导吗?
答案 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.property
和self
,因此保留了self但instanceVariable
和{{1}的值在块的代码中查询它们,它们将分别在执行时返回它们的值self.property
和"ivar-after"
。另一方面,在创建块时保留"prop-after"
,并且此时它的值被复制,因此最后localVariable
将显示NSLog
。
"locvar-before"
。在块的代码中直接使用局部变量时,会保留局部变量。
注意:我建议你观看WWDC'11和WWDC'12视频,讨论这个主题,它们真的很有启发性。
答案 1 :(得分:3)
这是否适当使用“按值”/“按参考”术语? 它至少类似于典型用法。将值复制到局部变量就像将值复制到堆栈中一样;使用ivar就像将指针传递给存储在其他地方的值。
我没看到“引用”示例中是如何引用self的? 在方法中使用实例变量时,引用隐含self
。实际上有些人会写self->foo
代替foo
来访问ivar,只是为了提醒自己foo
是一个ivar。我不建议这样做,但重点是foo
和self->foo
意味着相同的事情。如果您访问块内的ivar,将保留self
以确保在块的持续时间内保留ivar。
您是否有任何关于何时可能想要以某种方式做事的指导? 根据参考传递来思考是有用的/通过值区分在这里。正如AliSoftware所解释的那样,在创建块时会保留局部变量,就像在调用函数时复制通过值传递的参数一样。通过self访问ivar,因为通过引用传递的参数是通过指针访问的,因此在您实际使用它之前不会确定它的值。
这似乎会为其他变量声明带来许多额外的代码吗? 块已成为该语言的一小部分功能现在,我没有注意到这是一个问题。更常见的是,您需要相反的行为:本地声明的变量,您可以在块(或多个块)中进行修改。 __block
存储类型使这成为可能。
似乎可以更容易维护,因为最终可能最终会出现无意识保留对象的大杂烩,因此可能会更难以声明彼此内部的块? 让一个或多个块在需要时保留一个对象没有任何问题 - 只要使用它的块终止就会释放该对象。这完全符合通常的Objective-c手动内存管理理念,每个对象都只担心平衡自己的保留。避免多层嵌套块的更好理由是,这种代码可能比它需要的更难理解。