Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a __block variable is moved to the heap BEFORE the block is copied? [closed]

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
like image 761
jcccn Avatar asked Feb 26 '13 05:02

jcccn


2 Answers

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.

like image 153
CodaFi Avatar answered Oct 23 '22 02:10

CodaFi


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.

like image 32
newacct Avatar answered Oct 23 '22 03:10

newacct