According to bbum:
2) Blocks are created on the stack. Careful.
Consider:
typedef int(^Blocky)(void); Blocky b[3]; for (int i=0; i<3; i++) b[i] = ^{ return i;}; for (int i=0; i<3; i++) printf("b %d\n", b[i]());
You might reasonably expect the above to output:
0
1
2But, instead, you get:
2
2
2Since the block is allocated on the stack, the code is nonsense. It only outputs what it does because the Block created within the lexical scope of the for() loop’s body hasn’t happened to have been reused for something else by the compiler.
I don't understand that explanation. If the blocks are created on the stack, then after the for loop completes wouldn't the stack look something like this:
stack:
---------
^{ return i;} #3rd block
^{ return i;} #2nd block
^{ return i;} #1st block
But bbum seems to be saying that when each loop of the for loop completes, the block is popped off the stack; then after the last pop, the 3rd block just happens to be sitting there in unclaimed memory. Then somehow when you call the blocks the pointers all refer to the 3rd block??
Mike Ash provides the answer:
In bbum's example, the scope of the block is the
for-loop's
enclosing braces(which bbum omitted):So, each time through the loop, the newly created block is pushed onto the stack; then when each loop ends, the block is popped off the stack.
Yes, I think that's the way that it must have worked in the past. However, now it appears that a loop does not cause the block to be popped off the stack. Now, it must be the method's braces that determine the block's enclosing scope. Edit: Nope. I constructed an experiment, and I still get different addresses for each block:
AppDelegate.h:
AppDelegate.m:
That looks to me like blocks are allocated on the heap.
Okay, my results above are due to
ARC
. If I turn offARC
, then I get different results:That looks like stack allocation. Each pointer points to the same area of memory because after a block is popped off the stack, that area of memory is reused for the next block.
Then it looks like when the first block was called it just happened to get the correct result, but by the time the 2nd block was called, the system had overwritten the reclaimed memory resulting in a junk value? I'm still not clear on how calling a non-existent block results in a value??