__block specifier

There are two ways to solve the problem of not being able to assign values to automatic variables outside the Block

  1. C static variable/static global variable/global variable, allowing Block to rewrite the value.
  2. Use the __block specifier

The first code is as follows:

int global_val = 1;

static int static_global_val = 2;

int main(int argc, char * argv[]) {
    
    static int static_val = 3;
    void (^blk)(void) = ^{
        global_val *=1;
        static_global_val *=2;
        static_val *=3;
    };
    
    return 0;
}

Copy the code

The converted code looks like this:

struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; int global_val = 1; static int static_global_val = 2; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int *static_val; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int *static_val = __cself->static_val; // bound by copy global_val *=1; static_global_val *=2; (*static_val) *=3; } static struct __main_block_desc_0 { size_t reserved; size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; int main(int argc, char * argv[]) { static int static_val = 3; void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val));return 0;

}

Copy the code

Global and static global variables are exactly the same as before, with the static variable static_val being converted and accessed using a pointer to the static variable. Pass a pointer to the static variable static_val to the __main_bock_IMPL_0 constructor and save it.

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_val = __cself->static_val; // bound by copy

        global_val *=1;
        static_global_val *=2;
        (*static_val) *=3;
    }
Copy the code

In fact, automatic variables of intercepted objects beyond their specific variable scope can be stored on a value Block generated by the Block syntax. At the end of the scope of the variable, the original automatic variable is discarded, because any variable that exists outside the scope of the variable in the Block will not be able to access the original automatic variable through a pointer, just like a static variable.

Second method: Use the __block specifier (the __block storage domain class specifier) the C language has the following storage domain class specifiers:

  • typedef
  • extern
  • static
  • auto
  • register

Use the C storage class, linking, and Memory Management summary to explain the storage domain class specifier

A storage class describes a variable (a data object) : storage period, scope, link

Scope: A C variable scope can be a block of code scope (blocks of code inside the function defined in the variable, parameter, called the local variables), the function prototype scope, from variable definition site to prototype statement at the end of), or the document scope (a variables defined outside all functions, from the definition to include the end definition file is visible, Function scope (applicable only to tags used by GOTO statements). Function scope means that the GOTO tag in a particular function is visible to code anywhere in that function, no matter in which code block the tag appears.

Link: A C variable has one of the following links, external, internal, or empty.

  • Variables with block scope and function prototype scope are empty links that are private to the block and function prototype in which the definition resides
  • Variables with external links can be used in any file of a multifile program, and multiple files can be shared.
  • Variables with internal links can be used anywhere in a file and are private to that file.
  • Variables with file scope can be internal links or external links.
  • If a variable with file scope is modified static, it is an internal link; otherwise, it is an external link.

Storage period: A C variable has one of two storage periods, a static storage period (which persists during program execution) and an automatic storage period

  • Static storage period: lasts for the duration of program execution. Variables with file scope (whether internal or external) have this storage period. Note that static for file-scoped variables indicates that the link type is not a storage period.
  • Automatic storage period: Variables with block scope are generally automatic storage periods.
Storage class period scope link A declarative way
automatic automatic The code block Null links Within the code block
register automatic The code block Null links Inside the code block, use the keyword register
Static with external links static file External links Out of all functions
Static with internal links static file Internal links In addition to all functions, use the keyword static
Static of empty links static The code block empty Inside the code block, use the keyword static

Automatic variables: Variables that belong to the automatic storage class by default with or without auto modification have automatic storage cycles, code block scope, and empty links. By default, any variable defined in the code fast or in the function header belongs to the automatic storage class.

If you use a variable with the same name as the outer variable in the inner layer, the inner layer overwrites the outer variable, and when the program leaves the inner block, the outer variable reverts to its role and previous value.

For automatic variables, they are not automatically initialized unless they are displayed.

Register variable: declared by register and placed in register rather than memory, so its address cannot be obtained. Note that sometimes a variable is requested to be placed in a register, but the number of registers is not always satisfied, so the register variable becomes a normal variable, but still cannot be addressed.

Static variables with block scope: With static modification, if it is not realistic to initialize it, it is automatically initialized to 0 (providing the static period), declared within the code block (providing block scope and empty links), and created local variables with block scope and static storage. These variables have block scope, empty links, and static storage periods. Variables, once defined, persist until the end of the program.

Static variables with external links: have file scope, external links, static periods (external storage classes, external variables). As an additional note, external variables can only be initialized once, and at definition time.

Static variables with internal links: created with static defined outside all functions, static storage period, file scope, internal links.

The storage class specifier auto indicates that a variable has an automatic storage period. This specifier can only be used in variable declarations with block scope.

Register can only be used with variables that have block scope. Request a variable to be stored in a register while being used quickly, but cannot get the address of the change.

Static is used when declaring a variable with block scope to give the variable a static storage period, allowing it to exist and retain its value while the program is running. The variable still retains block scope and empty links. Makes the variable have an internal link if used with a file scoped variable declaration.

Extern specifies that a variable is declared that is defined elsewhere.

Const makes data immutable, can only be declared and cannot change its value later. When used in Pointers, the position of const determines whether the pointer itself remains unchanged or the data to which it points.

Volatile indicates that data may be modified by agents in addition to the program, primarily for compiler optimization.

Restrict can only be used to modify Pointers that are considered to provide unique access to the block to which they point.


The __blocks specifiers are similar to static, auto, and register, and are used to specify which storage field to set the variable value to. For example, auto means stored in the stack as an automatic variable, and static means stored in the data area as a static variable.

Follow the code for the __block specifier provided earlier:

#include <stdio.h>int main(int argc, char * argv[]) { __block int val = 10; void (^blk)(void)=^{val=1; }; }Copy the code

Converted code:

struct __Block_byref_val_0 { void *__isa; __Block_byref_val_0 *__forwarding; int __flags; int __size; int val; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_val_0 *val; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { __Block_byref_val_0 *val = __cself->val; // bound by ref (val->__forwarding->val)=1; } static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/); } static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/); } static struct __main_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); void (*dispose)(struct __main_block_impl_0*); } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0}; int main(int argc, char * argv[]) { __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 10}; void (*blk)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344)); }Copy the code

Among them

__block int val = 10;

Copy the code

Converted to:

__attribute__((__blocks__(byref))) __Block_byref_val_0 val = {
	(void*)0,
	(__Block_byref_val_0 *)&val, 
	0, 
	sizeof(__Block_byref_val_0), 
	10
};
Copy the code

An instance of the __Block_byref_val_0 structure. The structure is declared as follows:

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

Assigning val to 1 in main converts to:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

	__Block_byref_val_0 *val = __cself->val; 
	// bound by ref
	
	(val->__forwarding->val)=1;
	
}

Copy the code

The __main_block_IMPL_0 struct instance of the Block holds a pointer to the __Block_byref_val_0 struct instance of the __block variable.

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_val_0 *val; // byref // __main_block_IMPL_0 struct instances hold Pointers to __Block_byref_val_0 struct instances of __block variables. __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

The __forwarding member of the __Block_byref_val_0 structure instance holds a pointer to the instance itself. Access the member variable val via the member variable __forwarding. The member variable val is a variable held by the instance itself, which is equivalent to the original automatic variable.

Also, the __Block_byref_val_0 structure of the __block variable is not in the __main_block_IMPL_0 structure of the Block. This is done in order to use the __block variable in multiple blocks.

int main(int argc, char * argv[]) { __block int val = 10; void (^blk0)(void)=^{val=0; }; void (^blk1)(void)=^{val=1; }; }Copy the code

Converted code:

int main(int argc, char * argv[]) {

    __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 10};
    void (*blk0)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));
    void (*blk1)(void)=((void (*)())&__main_block_impl_1((void *)__main_block_func_1, &__main_block_desc_1_DATA, (__Block_byref_val_0 *)&val, 570425344));

}


Copy the code

Both blocks use Pointers to the __Block_byref_val_0 structure instance val. In this way, the same __block variable can be used from multiple blocks. It is also possible to use more than one __block variable from a Block by adding arguments to the Block constructor and member variables.


Block storage domain

Block is converted to an automatic variable of the struct type of a Block, and a __block variable to an automatic variable of the struct type of a __block variable. An automatic variable of a struct type is an example of that struct generated on the stack.

The name of the The essence
Block Struct instance of Block on stack
__block variable Struct instance of a __block variable on the stack

Also, as you can see from the transformed code below, blocks are Objective-C objects. When a Block is considered an OC object, the Block’s class is _NSConcreteStackBlock.

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_val_0 *val; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

Similar classes are:

  • _NSConcreteGlobalBlock
  • _NSConcreteStackBlock
  • _NSConcreteMallocBlock
class Sets the storage domain of the object
_NSConcreteStackBlock The stack
_NSConcreteGlobalBlock The data area of the program (.data area)
_NSConcreteMallocBlock The heap

The memory occupied by a C/C++ compiled program is divided into the following parts:

  1. Stack: Automatically allocated by the compiler to hold function parameter values, local variable values, etc. It operates like a stack in a data structure.
  2. Heap: Usually allocated and released by the programmer. If not released by the programmer, the program may be reclaimed by the OS at the end of the program. Note that this is not the same thing as a heap in a data structure, but is distributed in a similar way to a linked list.
  3. Global (static) : Global variables and static variables are stored together. Initialized global variables and static variables are stored in one area, and uninitialized global variables and uninitialized static variables are stored in another area adjacent to each other. The program is released by the system after completion.
  4. Literal constants: This is where constant strings are stored and released by the system at the end of the program
  5. Program code area – the binary code that holds the function body.

When Block syntax is used in place of global variables, the generated Block is an _NSConcreteGlobalBlock class object.

Such as:

void (^blk)(void) = ^{printf("Global Block\n"); }; int main(int argc, char * argv[]) { }Copy the code

After the transformation:

struct __blk_block_impl_0 { struct __block_impl impl; struct __blk_block_desc_0* Desc; __blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteGlobalBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

The Block class is the _NSConcreteGlobalStack class. This Block means that the Block is set in the data area of the program with the struct instance. Because automatic variables cannot be used where global variables are used, there is no interception of automatic variables. Thus, the contents of a Block’s struct instance do not depend on the state at execution, so an instance is required throughout the program. So just set the Block with the struct instance in the same data area as the global variable.

Only when an automatic variable is intercepted does the value that the Block intercepts with the struct instance change depending on the state at execution time.

For example, the following code uses a Block syntax multiple times, but the value of the automatic variable intercepted in each loop is different.

int main(int argc, char * argv[]) {
    
    typedef int (^blk_t)(int);
    
    for(int rate = 0; rate<10; ++rate){ blk_t blk = ^(int count){returnrate*count; }; }}Copy the code

After the transformation:

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int rate; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _rate, int flags=0) : rate(_rate) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static int __main_block_func_0(struct __main_block_impl_0 *__cself, int count) { int rate = __cself->rate; // bound by copyreturn rate*count;
        }


int main(int argc, char * argv[]) {

    typedef int (*blk_t)(int);

    for(int rate = 0; rate<10; ++rate){ blk_t blk = ((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, rate));  }}Copy the code

In the following code, when the automatic variable is not intercepted, the Block intercepts exactly the same value each time with the struct instance.

int main(int argc, char * argv[]) {
    
    typedef int (^blk_t)(int);
    
    for(int rate = 0; rate<10; ++rate){ blk_t blk = ^(int count){returncount; }; }}Copy the code

After the transformation:

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static int __main_block_func_0(struct __main_block_impl_0 *__cself, int count) {return count;
        }
        
int main(int argc, char * argv[]) {

    typedef int (*blk_t)(int);

    for(int rate = 0; rate<10; ++rate){ blk_t blk = ((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); }}Copy the code

That is, even when Block syntax is used inside a function and not where a wide-area variable is written, blocks can be set in the data area of a program with a struct instance, as long as the Block does not intercept automatic variables.

Although the code for the clang conversion is the _NSConcreteStackBlock class object, the implementation is different.

  • Block syntax is used to describe global variables
  • Block syntax expressions that do not use automatic variables that should be intercepted

In this case, Block is the _NSConcreteGlobalBlock class object. That is, blocks are configured in the data area of the program. The other Block syntax generates a Block object of the _NSConcreteStackBlock class and sets it on the stack.

The _NSConcreteMallocBlock class addresses the following two problems

  1. The reason a Block can exist outside the scope of a variable
  2. The reason for the existence of the __block variable in the structure member __forwarding

A Block configured on a global variable can be safely used by a pointer from outside the variable scope, but a Block set on the stack is discarded if the variable scope to which the gas belongs ends. Since a __block variable is also configured on the stack, the scope of the variable to which it belongs ends, and the __block variable is also discarded.

Blocks provides a way to solve this problem by copying Blocks and __block variables from the stack to the heap. Copies blocks configured on the stack to the heap so that blocks on the heap continue to exist even after the variable scope described by the Block syntax ends.

The Block copied to the heap writes the _NSConcreteMallocBlock class object to isa, the member variable of the Block’s struct instance.

impl.isa = &_NSConcreteMallocBlock;
Copy the code

The __block variable can be accessed correctly whether it is configured on the stack or heap using the structure member variable __forwarding.

Copying method provided by Blocks: When ARC is in effect, in most cases the compiler makes the appropriate judgment and automatically generates code that copies Blocks from the stack to the heap.

typedef int (^blk_t)(int);

blk_t func(int rate){
    return ^(int count){
        return rate * count ;
    };
};

int main(int argc, char * argv[]) {
       
}
Copy the code

Conversion error

/var/folders/6_/v3tcjsp9277_82bgpvhr8l6c0000gn/T/main-29bba4.mi:441:12: error: 
      returning block that lives on the local stack
Copy the code

The reason is that the source code returns a Block function configured on the stack, that is, the variable scope ends when the caller is returned from the function during program execution, so the Block on the stack is also deprecated.

But the source code can be converted by the corresponding ARC compiler as follows:

blk_t func(int rate)
{
	blk_t temp = &__func_block_impl_0(__func_block_func_0,&__func_block_desc_0_DATA,rate);
	temp = objc_retainBlock(temp);
	
	return objc_autoreleaseReturnValue(temp);
}
Copy the code

Objc4 runtime library runtime/objc-arr.mm objc_retainBlock is the _Block_copy function.

/* * Assigns blocks generated by Block syntax, that is, blocks configured on the stack, to the equivalent of the Block type, temp, using a struct instance. */ temp = _Block_copy(temp); /* * _Block_copy copies blocks on the stack to the heap. * After copying, the addresses of the pairs are copied as Pointers to the variable temp. * /returnobjc_autoreleaseReturnValue(temp); /* * Register the Block on the heap as an OC object in autoReleasepool, and return that object. * /Copy the code

In most cases, the compiler makes the appropriate judgment and copies blocks from the stack to the heap. Then there are the following situations that cannot be judged:

  • When a Block is passed to an argument of a method or function

But if the parameters passed through are properly copied in a method or function, you do not need to manually copy before calling the method or function. The following methods or functions do not need to be manually copied:

  1. Cocoa framework methods with usingBlock in their names
  2. Grand Central Dispatch的API

For example, USES the NSArray class instances of enumrateObjectsUsingBlock method and dispatch_async function, don’t have to manually copy. In contrast, manual copying is required when passing blocks on the initWithObjects instance method of the NSArray class.

- (void)viewDidLoad {
    [super viewDidLoad];

    id obj = [self getBlockArray];
    typedef void (^blk_t)(void);
    
    blk_t blk = (blk_t)[obj objectAtIndex:0];
    
    blk();
    
}


-(id) getBlockArray{
    int val = 10;
    return [NSArray arrayWithObjects:^{NSLog(@"blk0:%d",val); },^{NSLog(@"blk1:%d",val); }, nil]; }Copy the code

Running this code finds an exception, which is due to the Block on the stack being discarded when the getBlockArray execution ends. Modify the getBlockArray method as follows, but copying blocks from the stack to the heap is CPU intensive.

-(id) getBlockArray{
    int val = 10;
    return [NSArray arrayWithObjects:[^{NSLog(@"blk0:%d",val); } copy],[^{NSLog(@"blk1:%d",val); } copy], nil]; }Copy the code
Block of the class Configuration storage domain for replica resources Print effect
_NSConcreteStackBlock The stack Copy from stack to heap
_NSConcreteGlobalBlock The data area of the program Do nothing
_NSConcreteMallocBlock The heap Reference count increment

As you can see from the table above, no matter where the Block configuration is, copying with copy does not cause any problems. In case of uncertainty, just call copy.

ARC cannot explicitly release a copy file, so there is no problem calling copy multiple times.

blk = [[[[blk copy] copy] copy] copy];
Copy the code

The explanation is as follows:

{/** assign blocks configured on the stack to the variable BLK. */ blk_t temp = [blk copy]; /** Copies blocks configured on the heap into the variable temp, which holds strongly referenced blocks. */ blk = temp; /* * Assign the Block of the temp variable to the BLK variable, which holds a strongly referenced Block. * Because the Block originally assigned is configured on the stack, it is not affected by this copy. * Block is held by the variables BLK and temp. */} /* * The temp variable is deprecated because the scope of the variable is over, its strong reference is invalidated and the held Block is freed * The Block is not freed because it is held by BLK. Blk_t temp = [BLK copy]; blk_t temp = [BLK copy]; /* * Blocks configured on the heap are assigned to the variable temp, which holds strongly referenced blocks. */ blk = temp; /* * A strong reference to the first Block assigned to the BLK variable is invalidated and the Block is freed. * Block is not deprecated because it is held by the variable temp. * The BLK variable assigns a Block to the temp variable, which holds a strong reference to the Block. * Block is held by the variables BLK and temp. */} /* * The temp variable is deprecated because the scope of the variable is over, its strong reference is invalidated and the held Block is freed * The Block is not freed because it is held by BLK. */ /* * Repeat the process */Copy the code

The __block variable stores fields

__block variables are also affected when blocks that use them are copied from the stack to the heap.

Configure storage domains for __block variables The effect of a Block being copied from stack to heap
The stack Copied from the stack to the heap and held by the Block
The heap Be Block to retain

If a __block variable is used in a Block, when the Block is copied from the stack to the heap, all __block variables used must also be configured on the stack; These __blocks are also all copied from the stack to the heap. At this point, the Block holds the __block variable. Even if the Block is copied to the heap, copying the Block has no effect on the __block variable used.

When using a __block variable in multiple blocks, it is also configured on the stack because all blocks are configured on the stack first. When any Block is copied from stack to heap, the __block variable is copied from stack to heap and held by the Block. When the remaining blocks are copied from the stack to the heap, the copied Block holds the __block variable and increases the reference count of the __block variable.

If a Block configured on the heap is deprecated, the __block variable it uses is released.

Blocks that use __block variables hold __block variables. If a Block is deprecated, the __block variable it holds is released. This is exactly the same as OC’s reference-counting memory management.

The __block variable uses the structure member variable __forwarding, which can be accessed correctly whether the __block variable is configured on the stack or heap.

__block int val =0;
    
void (^blk)(void)=[^{++val;} copy];
    
++val;
    
blk();
    
NSLog(@"%d",val);

Copy the code

The execution result is as follows:

The 2017-12-13 20:33:37. 791190 + 0800 ImageOrientation [6953-2428079] 2Copy the code

In functions that transform Block syntax, the variable val is used as a struct entity for a __block variable copied to the heap, and the variable val, which cannot be used as a Block variable, is used as a struct instance for a __block variable copied on the stack.

However, when a __block variable on the stack is copied from the stack to the heap using a struct instance, the value of the member variable __forwarding is replaced by the address of the struct instance of the __block variable on the target heap. With this feature, the same __block variable can be accessed without any problems, whether it is used in or out of the Block syntax, or whether the __block variable is configured on the stack or heap.