Scopes, man. Scopes.
Rewrite that as:
void georgeClinton() {
int (^b[3])(); // iirc
// georgeClinton's scope
for (int i=0; i<3; i++) {
// for's scope
b[i] = ^{ return i;};
}
}
On every pass through that for() loop, for's scope is effectively a new scope. But, of course, scopes are on the stack.
When you call georgeClinton(), you effectively push georgeClinton()'s scope onto the stack. And when georgeClinton() returns with some funky goodness, georgeClinton()'s scope is popped off the stack, leaving the stack in whatever state it was in when the push happened (with a potential modification for the return value).
A for()
loop is the same thing; each iteration pushes state onto the stack and pops it off at the end of the iteration.
Thus, if you store anything on the stack in an iteration of a for() loop, like a block, that thing will be destroyed at the end of the iteration. To preserve it, you must move it to the heap (where you control the lifespan of the state of any given allocation).
The key being that a block typed variable is really a pointer; it is a reference to a structure that defines the block. They start on the stack for efficiency and this can lead to subtle issues like this one.
Note that a block is really two things; it is a reference to the bit of immutable code that implements the block (which is really just like a function pointer) and it is a description of the data captured in the block and how that data is to be moved to the heap when copied.
That is, a block is the combination of data and code. Code that never changes. Data that is captured as the execution pointer passes over the expression that defines the block (i.e. the block "closes over" the current state of execution).
It is that latter bit that trips you up; when a block is created on the stack, it is created with slots to hold captured data, also on the stack.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…