I know that a __block
variable will be moved to the heap from the stack if a Block accessing it was copied. But the following test code show me that the __block
variable is moved to the heap before the Block's copying.
That is, the four outputs are: stack => heap => heap => heap, which is not my expected result: stack => stack => stack => heap.
Could someone straighten me out?
__block int x = 0;
int *pointerToX = &x;
//1. It's on the stack
NSLog(@"x's location is on the stack: %p", &x);
int (^block)() = ^{
x += 1;
return x;
};
//2. I think its stack, but it's heap
NSLog(@"x's location is on the %@: %p", (&x == pointerToX ? @"stack" : @"heap"), &x); //it's heap not stack
block();
//3. I think its stack, but it's heap
NSLog(@"x's location is on the %@: %p", (&x == pointerToX ? @"stack" : @"heap"), &x); //it's heap not stack
block = [block copy]; // The variable x will be moved to the heap
//4. I think its stack, but it's heap
NSLog(@"x's location is on the %@: %p", (&x == pointerToX ? @"stack" : @"heap"), &x); //heap
Let me preface this by saying: Blocks are weird.
Now, when you start out, you've declared a variable x, and also prefixed it with __block
. What the heck is __block
anyhow? Well, for objects captured in the lexical scope of the block, variables are -retain
'ed so as to guarantee they are around when the block is executed. But for primitive variables, blocks secure their values by forcing them to be passed by const
value rather than by reference. By prepending __block
, you've given the compiler free reign to move your variable "magically" from the stack to the heap when the block is copied. To be clear, __block
variables are, in fact, stack allocated, but they are moved to the heap (malloc()
'd) when the block is copied.
But what about the weird changes in the location of x? Well, back to __block
again. Because you're not using a const
reference to x like a normal variable, blocks use a (slightly annoying) trick: A block creates a pointer to any __block
variable, and if that variable is mutated, it is dereferenced. Ta da! Your variable didn't move from the stack to the heap, the block merely dereferenced the pointer to it and moved it in memory!
So, really, you're confused about where and when your variables are moved around. Your example is logging the correct values.
Your expected output is based on your assumption that the block is not copied until step 3-4. However, nothing in the Blocks specifications guarantees that that would be the case.
Yes, the block will be copied at the latest when you explicitly call -copy
on it. But why can't it be copied earlier? It is never wrong to copy a block earlier. Therefore, when exactly a block is copied is undefined, and you should not depend on it.
Some recent versions of the compiler under ARC may be conservative and copy a block immediately after it is created. There is nothing wrong with that. Again, if it does that, it would be an implementation detail, and other compilers or future versions may do something different.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With