Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free.

I have the following code under ARC:

-(void) foo {
    __block int spam = 42;
    self.myProperty = ^{
        spam++;
    }
    self.myProperty(); // Increment first time
}

-(void) bar {
     self.myProperty(); // Increment second time
}

When "Increment first time" is called, ObjC uses pointer to spam (which resides on stack) to increment it, right? After it, foo stack is thrown out, so pointer is not valid anymore.

What will bar do? What should happen when I call it?

Everything is clear about objects, since they are created on heap and block copying (which takes place in the moment of property assignment) leads to retain call. But what is about auto vars?

I can use debugger to find the answer, but I want to fully understand it: which part of Objc/clang specification covers that?

Update: A used "&" to get address of my variable and found that address get changed in the moment I assign block to my property (actually at the moment when block is copied). I believe that is the moment from my variable was moved from stack to heap.

-(void) foo {
    __block int spam = 42;
    int* addrOfSpam = &spam;

    *addrOfSpam = 43; // OK, spam = 43 now
    self.myProperty = ^{ // The moment when spam is moved from stack to heap
        spam++;
    };
    *addrOfSpam = 44; // Fail, spam not changed  and 'addrOfSpam' points to nowhere

    spam++; // Spam looks like auto/stack var here, BUT IT IS NOT! 
            // It is on heap now, and "spam" is changed by compiler to:
            //  *(newAddressOfSpamInHeap_OnlyCompilerKnowsWhere)++;
}
share|improve this question
    
It's good to want to understand but try it and update your question with what happens from your tests. –  rmaddy Aug 8 at 22:09

2 Answers 2

The salient passage in the doc is here. (boldface added by me)

__block variables live in storage that is shared between the lexical scope of the variable and all blocks and block copies declared or created within the variable’s lexical scope. Thus, the storage will survive the destruction of the stack frame if any copies of the blocks declared within the frame survive beyond the end of the frame (for example, by being enqueued somewhere for later execution). Multiple blocks in a given lexical scope can simultaneously use a shared variable.

The block qualifier places the variable in a scope that will persist at least as long as the block that refers to it. So the first and second invocations of the block are identical with respect to the variable. After the second call, spam should equal 44.

share|improve this answer
1  
Thanks. So, If I use spam in several blocks, it will survive as long as at least one block exists? Does it mean I now have "automatic reference counting" to primitive variable? Not only objects may have reference counting but primitives as well? –  user996142 Aug 8 at 23:59
    
@user996142 This is how I understand it: if the block is an Obj-C object instance, then spam is an instance variable of that block instance. In other words, spam isn't reference counted, but it belongs to an object instance that is reference counted. –  Darren Aug 9 at 0:03
    
Yes, but I suspect the memory model for the block scalar is more like static vars. in other words, it isn't ref counted and can remain allocated longer than all of the blocks. –  danh Aug 9 at 0:03
    
@Darren - your understanding of how it works would keep the memory around long enough, but it is unlikely to be literally true - remember that the scalar value is shared between the block(s) and the context of its (their) definition. In other words, to the block, spam can't be an int instance variable, but an int* to a variable in the containing class, one that outlives the original method... in other words, more like (or exactly like, I suspect) a static int. –  danh Aug 9 at 0:40

First of all, __block is a storage qualifier for variables, and it the same way for all types of variables. Whether it's a variable of primitive type, pointer-to-object type, or whatever (by the way, a variable cannot have "object type"); doesn't matter. Variables of all types normally reside on the stack if they are local variables of a function. Referring to a local variable of pointer-to-object type after its scope has exited equally results in undefined behavior.

About your question, the answer is that __block variables are not only stored on the stack. They can also be stored on the heap. How it works is an implementation detail but it's guaranteed by the specification to be valid to be used in all the blocks that capture it.

If you want to know how it is actually implemented, you can read Clang's Block implementation specification (although I think there are some typos). Essentially, when you have a __block variable, you actually have a data structure, where the variable is a field. And the data structure also has a pointer to keep track of where the current version of itself is. Accesses to the variable are implicitly translated by the compiler to access it through these levels of indirection.

Just like with blocks, this data structure starts out on the stack for optimization, but can be "moved" to the heap (it gets moved the first time any block that captures it gets moved to the heap, because that's when its lifetime might need to exceed the scope it was created in). When it's "moved" to the heap, the pointer that says where it is is updated, so that people will know to use the new copy. This data structure, when in the heap, is memory-managed by a system of reference counting.

Blocks that capture __block variables have a copy of the "current version" pointer, which is updated when the data structure is moved, so they know where to access the variable both before and after the move.


About your tests, you have to be really careful about taking the address of a __block variable, because the location of the variable moves over its lifetime! (something that doesn't usually happen in C).

When you first take the address of spam, it is still on the stack, so you have the address of the stack version of it. spam is captured in a block which is then assigned to the property myProperty whose setter copies the block, moving it to the heap and also moving spam to the heap. addrOfSpam still points to the stack version of the variable which is no longer the version being used; using it to change the integer is changing the wrong version of the variable, because everyone is using the heap copy now.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.