我知道在堆栈中创建了块。但是,由于我对堆栈和局部变量知之甚少,我无法理解为什么我应该将块移动到堆中以获得预期的结果。直观地,我觉得块代码块在堆栈中只有1个实例,这段代码引用本地变量i
3次。如果我将它复制到堆,它将有3个不同的实例,每次它将在复制过程中捕获3个不同的i
值。但我真的想更多地了解堆栈中的块代码,堆和引用局部变量。
for (int i=0; i<3; i++)
b[i] = ^{ return i;};
for (int i=0; i<3; i++)
printf("b %d\n", b[i]());
答案 0 :(得分:13)
范围,伙计。作用域。
将其重写为:
void georgeClinton() {
int (^b[3])(); // iirc
// georgeClinton's scope
for (int i=0; i<3; i++) {
// for's scope
b[i] = ^{ return i;};
}
}
在每次传递for()循环时,for的范围实际上是一个新的范围。但是,当然,范围在堆栈中。
当你调用georgeClinton()时,你有效地将 georgeClinton()的范围推送到堆栈上。当georgeClinton()返回时带有一些时髦的善良,georgeClinton()的范围是弹出离开堆栈,使堆栈处于推送发生时的状态(可能会对其进行修改)返回值)。
for()
循环是一回事;每次迭代都会将状态推送到堆栈并在迭代结束时将其弹出。
因此,如果你在for()循环的迭代中将任何东西存储在堆栈中,就像一个块一样,那个东西将在迭代结束时被销毁。要保留它,您必须将其移动到堆(您可以控制任何给定分配状态的生命周期)。
关键是块类型变量实际上是一个指针;它是对定义块的结构的引用。他们从堆栈开始提高效率,这可能导致像这样的微妙问题。
注意一个块实际上是两件事;它是对实现块的不可变代码位的引用(它实际上就像一个函数指针),它是对块中捕获的数据的描述以及该数据是如何形成的复制时移动到堆。
也就是说,块是数据和代码的组合。永远不会改变的代码。作为执行指针捕获的数据通过定义块的表达式(即块“关闭”当前执行状态)。
后一点让你绊倒;当在堆栈上创建一个块时,它会创建一个槽来保存捕获的数据,也可以在堆栈上。