为了理解块的词法范围,我编写了以下代码
typedef int (^ MyBlock)(void);
MyBlock b[3];
for (int i=0; i<3; i++) {
b[i]=^{return i;};
}
for (int i=0; i<3; i++) {
NSLog(@"%d",b[i]());
}
NSLog(@"----------------------------");
int j=0;
b[0]=^{return j;};
j++;
b[1]=^{return j;};
j++;
b[2]=^{return j;};
for (int i=0; i<3; i++) {
NSLog(@"%d",b[i]());
}
我预计两个块执行都会有2,2,2。
有人可以解释一下为什么会这样吗?
答案 0 :(得分:7)
我假设您一直在阅读bbum’s post on blocks,并且知道您的代码不正确,因为您没有将块从堆栈复制到堆中。
那说:
for (int i=0; i<3; i++) {
b[i]=^{return i;};
}
在每次迭代中执行以下操作:
b[i]
; {}
)已经结束,弹出堆栈中的任何内容并重置堆栈指针。堆栈在每次迭代开始时增长,并在每次迭代结束时收缩。这意味着所有块都在相同的内存地址中创建,即 A 。这也意味着b
数组中的所有元素最终都指向同一个块,即最后创建的块。您可以通过运行以下代码来测试它:
for (int i = 0; i < 3; i++) {
printf("%p", (void *)b[i]);
}
应该输出如下内容:
0x7fff5fbff9e8
0x7fff5fbff9e8
0x7fff5fbff9e8
所有元素都指向同一个块,最后一个块在内存地址 A = 0x7fff5fbff9e8中创建。
另一方面,当您执行以下操作时:
b[0]=^{return j;};
j++;
b[1]=^{return j;};
j++;
b[2]=^{return j;};
没有复合语句为所有块定义相同的范围。这意味着每次创建块时,其地址都在堆栈的下方,有效地为每个块分配不同的地址。由于所有块都不同,因此它们正确捕获j
的当前运行时值。
如果您按照前面所述打印这些块的地址,您应该得到类似于:
的输出0x7fff5fbff9b8
0x7fff5fbff990
0x7fff5fbff968
显示每个块位于不同的内存地址。
答案 1 :(得分:0)
用于迭代b [i]的块数组的i不是每个块中使用的i。您定义的块引用定义期间使用的i。并且我被改为2.然后你用另一个i迭代这些块,但是块仍然引用你在块的定义期间使用的原始i(现在保持值2),尽管我已经“死了“为了该计划的其余部分。 在第二种情况下,您的块也使用一个公共变量,但每次使用它之前都要更改它。
关键是:块始终与定义期间使用的变量相关联。第二个for循环中的i不是块所指的i。
当定义代码已经“死”时,可以调用块。为此,参考变量具有“扩展”实时。它们也可能被运行时移动到堆中。
查看WWDC 2010视频“会议206 - 在iPhone上介绍块和Grand Central Dispatch”。