The last article looked at the storage domain of blocks. In this article we’ll look at the storage domain of __block variables.

A __block variable cannot be declared as a global variable

When we declare __block as a global variable, the code looks like this:

// declare as a global variable
__block int global_val = 10;

int main(int argc, const char * argv[]) {
    ...
    return 0;
}
Copy the code

An error message is displayed with __block attribute not allowed, only allowed on local variables. Why do I get an error? In fact, it is easy to understand that the __block attribute is designed to solve the problem that local variables cannot be modified inside blocks. Global variables don’t have this problem, so don’t bother.

Storage domain for __block variables

__block attribute not allowed, only allowed on local variables __block attribute not allowed, only allowed on local variables This leads to the storage domain for __block variables and the effect that blocks have on __block variables when copied from the stack to the heap.

2.1 __block is stored on the stack

Let’s first imagine a scenario where a local variable (not an object) decorated by a __block attribute is stored in what region from creation to use by a stack BLock. __block is stored on the stack in two cases:

  • (non-object) when initialized;
  • Used by a stack BLock.

2.1.1 (Non-object) when initializing

When the __block variable is initialized, the code looks like this:

int main(int argc, const char * argv[]) {
    
    // declare a local variable
    __block int val = 10;
    // This local variable is used for address comparison
    int num = 10;
    
    NSLog(@"__block variable address: %p -- local variable address: %p", &val, &num);
    
    return 0;
}
Copy the code

The console print statement is as follows:

Address of __block variable: 0x7ffeefbff578 -- address of local variable: 0x7FFeefbff55cCopy the code

We can see that the address of the __block variable is next to the address of the ordinary local variable, so the newly initialized __block variable is stored on the stack.

2.1.2 Used by stack Block

The code used by a stack BLock variable is as follows:

int main(int argc, const char * argv[]) {
    
    // declare a local variable
    __block int val = 10;
    // This local variable is used for address comparison
    int num = 10;
    
    void(^ __weak block)(void) = ^{
        val = 11;
    };
    
    block();
    
    NSLog(@"__block variable address: %p -- local variable address: %p", &val, &num);
    
    return 0;
}
Copy the code

The console print statement is as follows:

Address of __block variable: 0x7ffeEFbff588 -- address of local variable: 0x7FFeEFbff56cCopy the code

We can see that the address of a __block variable is next to the address of a normal local variable, so __block variables used by the stack Block are stored on the stack (__block variables are not strongly referenced).

2.2 __block is stored on the heap

We know that when a __block variable stored on the stack is used by a stack BLock, it is not copied to the heap, so what happens when a __block variable is used by a heap BLock? So let’s explore that. The code:

int main(int argc, const char * argv[]) {
    
    // declare a local variable
    __block int val = 10;
    // This object is used for address comparison
    People *people = [[People alloc] init];
    
    void (^block)(void) = ^{
        val = 11;
    };
    
    block();
    
    NSLog(@"__block address: %p -- object address: %@", &val, people);
    
    return 0;
}
Copy the code

The console prints the following:

Address of __block variable: 0x100704828 -- address of object: <People: 0x100706B90 >Copy the code

We can see that when a __block variable stored on the stack is used by the stack BLock, the __block variable is copied to the heap (strongly referenced by the heap BLock). NSLog(@”__block address: %p — object address: %@”, &val, people); Here’s the code clang to see what’s going on:

NSLog((NSString *)&__NSConstantStringImpl__var_folders_0r_hkkmpct143n4wd3xxk0l1j8c0000gn_T_main_c842f2_mi_0, &(val.__forwarding->val), people);
Copy the code

Review the structure of the __block variable:

struct __Block_byref_val_0 {
  void *__isa;
__Block_byref_val_0 *__forwarding;
 int __flags;
 int __size;
 int val;
};
Copy the code

We can see that when we use the __block variable val, we actually use the value val.__forwarding->val.

So we can guess that when the __block variable is initialized on the stack, the __forwarding member variable initially points to the __block variable on the stack, but when the __block variable has a copy on the heap, the __forwarding member variable points to the __block variable on the heap. So we access the same __block variable both inside and outside the Block.

2.2 __block is used by multiple heap blocks

In fact, we have already discussed the storage fields of __block variables, namely stack and heap. So what happens when a __block variable is used by multiple heap blocks? The __block variable is essentially an object, so every time it is used by a heap BLock, it is strongly referenced. The reference count of the __block variable is +1, which is exactly the same as the reference count of OC memory management.


Configure storage domains for __block variables The effect of BLock assignment from stack to heap
The stack Copied from the stack to the heap and held by a Block, __forwarding refers to a __block object on the heap
The heap Held by Block, reference count +1