我希望在单个块的多个调用中重用对象引用,我很好奇:以下两种方法之间的实际区别是什么?
使用__block
变量:
__block Widget *widget = [self buildNewWidget];
for(Gadget *gadget in self.gadgets) {
[self useGadget:gadget withCallback:^{
if([widget isBroken]) {
widget = [self buildNewWidget];
}
gadget.widget = widget;
}];
}
使用static
变量:
for(Gadget *gadget in self.gadgets) {
[self useGadget:gadget withCallback:^{
static Widget *widget;
if(!widget || [widget isBroken]) {
widget = [self buildNewWidget];
}
gadget.widget = widget;
}];
}
显然,这两个代码块在语义上有所区别,但(实际上说)我相信它们做同样的基本工作。我的猜测是,从内存管理角度,性能角度或其他方面来看,存在差异。任何能够说明这些差异(或解释它们没有区别的原因)的见解都会有所帮助。
答案 0 :(得分:2)
一个例子胜过千言万语:
(是的,这是一个非常简单的例子,但它大致相当于你正在做的事情......)
for (int i = 0; i < 3; i++)
{
// Your example encompasses this scope,
// not taking into account that we may execute this code multiple times:
// Call the block
(^{
// Every instance/execution of this block will have the same object.
static Obj *o;
// Initialize static object
static dispatch_once_t once;
dispatch_once(&once, ^{
o = [Obj new];
});
NSLog(@"Object is: %@", o);
})();
}
// Output:
// Object is: <Obj: 0x100109fd0>
// Object is: <Obj: 0x100109fd0>
// Object is: <Obj: 0x100109fd0>
for (int i = 0; i < 3; i++)
{
__block Obj *o = [Obj new];
// Call the block
(^{
// This block uses the object from its enclosing scope, which may be different.
NSLog(@"Object is: %@", o);
})();
}
// Output:
// Object is: <Obj: 0x105100420>
// Object is: <Obj: 0x1003004f0>
// Object is: <Obj: 0x105300000>
答案 1 :(得分:1)
正如所写,这两个代码片段的工作方式不同,它们的最终结果不同。
第二组代码是等待发生的失败。如果由于使用静态变量,此代码同时在两个不同的线程上运行,则会失败。此代码也将失败,因为您从未初始化静态变量。第一次到达if
语句时,应用程序可能会崩溃。
由于循环的每次迭代看起来都取决于widget
的当前值,因此需要在循环之前初始化一个局部变量。由于需要在块内修改此变量,因此需要将变量设为__block
变量。这意味着您的第一组代码是正确的代码。
答案 2 :(得分:1)
出于本答案的目的,假设两个示例都包含在-(void)useGadgetsOnWidgets { ... }
。
假设ARC,您的应用程序是单线程的,并且代码是不可重入的(即useGadgetsOnWidgets
不调用自身),并且该方法返回后未使用该块,则有一个主要区别:
使用static
变量,widget
永远坚持下去。这意味着小部件可以在调用-useGadgetsOnWidgets
(可能是好的和坏的)之间重用,但也意味着Widget会永久保留。您可以通过将小部件拉出循环/阻止来更改此功能(我在开始时也将它更加类似于__block
版本:
-(void)useGadgetsOnWidgets {
static Widget *widget;
widget = [self buildNewWidget];
for(Gadget *gadget in self.gadgets) {
[self useGadget:gadget withCallback:^{
if([widget isBroken]) {
widget = [self buildNewWidget];
}
gadget.widget = widget;
}];
}
widget = nil;
}
有一个第三个变体有点线程安全,并假设在方法返回后不使用该块:
-(void)useGadgetsOnWidgets {
Widget *widget = [self buildNewWidget];
Widget ** pWidget = &widget;
for(Gadget *gadget in self.gadgets) {
[self useGadget:gadget withCallback:^{
if([*pWidget isBroken]) {
*pWidget = [self buildNewWidget];
}
gadget.widget = *pWidget ;
}];
}
}
这似乎比使用static
变量(实际上只是一个全局变量)稍微好一些,但它仍然很蹩脚。这也不是我想要给新手程序员教授的技术(但是再一次,也不是任何类型的多线程)。
编辑:对于您描述的问题,比其中任何一个更好的解决方案是将小部件缓存在self
上的ivar / property中:
-(Widget*)workingWidget {
// Assuming _cachedWidget is an ivar
if ([_cachedWidget isBroken]) {
_cachedWidget = [self buildWidget];
}
return _cachedWidget;
}
-(void)useGadgetsOnWidgets {
for(Gadget *gadget in self.gadgets) {
[self useGadget:gadget withCallback:^{
gadget.widget = [self workingWidget];
}];
}
}
答案 3 :(得分:-1)
__ block使变量在块内可用,就像它是全局变量一样。如果你在块中使用它,而不是被复制它将被引用,因为它就像全局它将仍然存活。但是下次你调用那个代码块时,另一个变量将被推入堆栈。
static使变量仅在作用域中可见,并且在程序的整个执行过程中仍然存在。但是,如果再次调用该代码块,变量将是相同的。