How does Objective-C deal with primitive __block variables on the stack when copying a block?

I have 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 First Time Increment is called, ObjC uses a spam pointer (which is on the stack) to increment it, isn't it? After that, the foo stack is thrown, so the pointer is no longer valid.

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

Everything is clear about objects, since they are created during heap and block copying (what happens when a property is assigned) leads to a call to retain . But what is auto-vari?

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

Update: "&" is used to get the address of my variable and found that the address changed the moment I assigned the block to my property (actually at the time of copying the block). I believe that this is the moment when 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)++; } 
+4
source share
2 answers

A notable passage in the document is here. (bold added by me)

__ Block variables live in a repository that is shared between the lexical region of the variable and all declared blocks and block copies or created within the lexical region of variables. Thus, the storage will survive the destruction of the stack frame if any copies of the blocks declared in the frame go beyond the frame (for example, by placing it somewhere for later execution). multiple blocks in a given lexical domain can simultaneously use a common variable.

The block qualifier places the variable in scope, which will be retained, at least until the block that refers to it. Thus, the first and second calls to the block are identical with respect to the variable. After the second call, spam should be 44.

+4
source

First of all, __block is a storage classifier for variables, and it is equally suitable for all types of variables. Is this a variable of primitive type, such as a pointer-object, etc. (By the way, a variable cannot have an "object type"); irrelevant. Variables of all types are usually on the stack if they are local variables of the function. Referring to a local variable of type "pointer-object" after its scope came out equally in undefined behavior.

About your question, the answer is that __block variables __block not only stored on the stack. They can also be stored in a heap. How it works is an implementation detail, but it is guaranteed by the specification for use in all the blocks that fix it.

If you want to know how this is implemented, you can read the Clang 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 track where the current version is. Access to a variable is implicitly translated by the compiler to access it through these levels of indirection.

Like blocks, this data structure starts on the stack for optimization, but can be β€œmoved” to the heap (it is moved for the first time when any block that captures it is moved to the heap, because when the life time may require an excess of volume, in which it was created). When it "moves" to the heap, the pointer that says where it is is updated to let people know to use the new copy. This data structure, when on the heap, is memory managed by a reference counting system.

Blocks in which __block variables are fixed 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 should be very careful in choosing the address of the __block variable, as the location of the variable moves throughout life! (which usually does not happen in C).

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

0
source

All Articles