Unable to understand the block's lexical scope
To understand the lexical scope of block, I have write the following code
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]());
}
- first time o/p is 2,2,2
- second time o/p is 0,1,2
I am expecting 2,2,2 for both of block execution.
Can anybody please explain me why is 开发者_StackOverflow社区it so?
I assume you’ve been reading bbum’s post on blocks and know that your code isn’t correct since you aren’t copying the blocks from the stack to the heap.
That said:
for (int i=0; i<3; i++) {
b[i]=^{return i;};
}
does the following in each iteration:
- Allocates space in the stack for a block variable. Let's say its memory address is A;
- Creates the block in the stack and assign its address (A) to
b[i]
; - At the end of the iteration, since the compound statement/scope (
{}
) has ended, pops whatever was in the stack and resets the stack pointer.
The stack grows at the beginning of each iteration, and shrinks at the end of each iteration. This means that all blocks are being created in the same memory address, namely A. This also means that all elements in the b
array end up pointing to the same block, namely the last block that was created. You can test this by running the following code:
for (int i = 0; i < 3; i++) {
printf("%p", (void *)b[i]);
}
which should output something like:
0x7fff5fbff9e8
0x7fff5fbff9e8
0x7fff5fbff9e8
All elements point to the same block, the last one created in the memory address A = 0x7fff5fbff9e8.
On the other hand, when you do the following:
b[0]=^{return j;};
j++;
b[1]=^{return j;};
j++;
b[2]=^{return j;};
there’s no compound statement that defines the same scope for all blocks. This means that each time you create a block its address is further down the stack, effectively assigning a different address to each block. Since all blocks are different, they correctly capture the current runtime value of j
.
If you print the address of those blocks as described earlier, you should get an output similar to:
0x7fff5fbff9b8
0x7fff5fbff990
0x7fff5fbff968
showing that each block is at a different memory address.
The i you use to iterate the array of blocks with b[i] is not the i that is used inside every block. The blocks you define reference the i that is used during definition. And that i is changed to 2. Then you iterate through those blocks with another i, but the block still refers to the original i (which by now holds the value 2) that you used during definition of the block although that i is already "dead" for the rest of the program. In the 2nd case your blocks use also a common variable but you change it before you use it every time.
The key is: The block is always associated to the variable that was used during definition. The i in the 2nd for-loop is not the i the blocks refer to.
Blocks can be invoke when the defining code is already "dead". For that the reference variables have an "extended" live. They might also be moved to the heap by the runtime.
Take a look at the WWDC 2010 video "Session 206 - Introducing Blocks and Grand Central Dispatch on iPhone".
精彩评论