IOS Block usage and implementation principles

“Objective-C advanced programming” is an interesting and difficult to understand the book, the book on the reference count, Block, GCD three concepts, interesting because of the principle of speaking, the realization of the part of the other iOS is rare in professional books. However, each chapter does not read 35 times or more difficult to understand. In this paper, some of the Block to do some simple notes, about the use of Block and part of the realization of the principle, a detailed explanation from the original book to find.

Block overview

Block: anonymous functions with automatic variables.
anonymous function: function not the function name, the content of a {} is the scope of the anonymous function.
automatic variables: a variable declared on the stack is not a static variable and global variable, can not be declared in the stack of anonymous functions used, but in Block can be. Although the use of
Block to declare a class, but Block offers a similar Objective-C like method can save scope variable values by member variables, those in Block to use but to {} the variables are declared in the scope is beyond {}, automatic variable Block intercept.

Block general concept

Block syntax

Block expression syntax:

Return value type (parameter list)

For example:

^ int (int count) {count + 1;}; return;

Among them, the part can be omitted:

  • Return type, example: ^ (int count) {count + 1;};
  • Parameter list is empty, can be omitted, for example: ^ {NSLog (@ No Parameter);}; that is, the simplest pattern syntax is: ^ {}}

Block type variable

Declare Block type variable syntax:

Return value type (^ variable name) (parameter list) = Block expression

For example, the following Declaration of a variable named BLK Block:

Int (^blk) (int) = ^ (int count) {count + 1;};

When the Block type variable acts as a parameter of the function:

- (void) func: (int (^) (int)) BLK {NSLog (@ Param:%@), BLK};}

With the help of typedef:

Typedef int (^blk_k) (int) - (void) func: (blk_k) BLK {NSLog (@ Param:%@);} (BLK);}

When a Block type variable returns a value:

((^) - int (int)) funcR return (int count) ^ {{return}} + +; count;

By typedef:

Typedef int (^blk_k); (int) - (blk_k) funcR return (int count) ^ {{return}} + +; count;

Automatic variable value

Block expressions can be used to intercept the value of the automatic variable.
interception: save the instantaneous value of automatic variables.
because it is the “instantaneous value”, so after the declaration of Block, even if the value of the Block to modify the value of the variable, it will not affect the value of the intercepted Block automatically variable.

Int i = 10; void (^blk) (void) (NSLog = ^{@ In block, I =%d, I);}; I = 20; //Block modify the variable I, does not affect the automatic variable BLK in Block (//i); revised to 20 after the implementation of print: In block, I = 10 (NSLog @ I =%d, I); / / print: I = 20

__block declaration symbol

The automatic variable values for the Block statement when intercepted instantaneous value, save cannot overwrite the value for re assignment of automatic variables, the need for additional __block descriptor variables in the statement, then the variable called __block variables.

__block int i = 10; //i __block variables in block deassign void (^blk) (void) (NSLog = ^{@ In block, I =%d, I);}; I = 20; (BLK); / / print: In block, I = 20 NSLog (@ "I =%d, I); / / print: I = 20

Automatic variable value for an object

When an automatic variable is an object of a class and is not modified by __block, it is not possible to re assign the variable in the Block, but it can modify the properties of the object.
if the object is a Mutable object, such as NSMutableArray, you can also add and delete elements of the NSMutableArray in the Block:

NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:@ "1", "2", @ nil]; NSLog (@ Array Count:%ld ", array.count); / / print Array Count:2 void (^blk) = [array (void) ^{removeObjectAtIndex:0]; //array = //Ok [NSNSMutableArray new] __block; / / no modification, fail to compile! }; BLK (); NSLog ("Array Count:%ld", @ array.count); / / print Array Count:1

Block implementation principle

Use Clang

Block is actually a very common C language source code to deal with: Block grammar containing the source code is first converted into C language compiler can handle the source code, and then as an ordinary C source code compiler.
using the LLVM compiler clang command can be included in the Objective-C Block code into the C++ source code, in order to explore its specific implementation:

Clang -rewrite-objc source file name

Note: if you use the command error: ‘UIKit/UIKit.h’ file found, can refer to the “Objective-C compiled into C++ code error” solution.

Block structure

When using Block, the compiler of the Block grammar how to convert?

Int main () {count = int; void (^ BLK) () = ^ () {NSLog (@ In Block:%d);}; BLK ();} (count)

As shown in the simplest Block using the code, after the clang conversion, the following parts can be obtained (code deletion and note added):

Static void __main_block_func_0 (struct __main_block_impl_0 *__cself) {int count = __cself-> count; / / bound by copy NSLog ((NSString) & __NSConstantStringImpl__var_folders_64_vf2p_jz52yz7x4xtcx55yv0r0000gn_T_main_d2f8d2_mi_0, count);}

This is the realization of a function, the contents of the corresponding Block {}, the content is as a function of C language processing, function parameters of __cself is equivalent to Objective-C in self.

Struct __main_block_impl_0 struct __block_impl struct __main_block_desc_0* {impl; Desc; / / Block, version information description of size int count; / / constructor __main_block_impl_0 (void *fp, struct __main_block_desc_0 *desc, int _count, int flags=0): count (_count) {impl.isa = & _NSConcreteStackBlock; / / statement in the function stack, for _NSConcreteStackBlock = impl.Flags flags; impl.FuncPtr = FP; Desc = DESC;}};

__main_block_impl_0 is main () function stack on the Block structure, where the __block_impl structure is declared as follows:

Struct __block_impl {void *isa; / / Class int Flags specified object; int Reserved; void *FuncPtr;};

__block_impl structure, that is, the structure of Block, can be understood as the class structure of Block.
look at the next main () function translation content:

Int main (int) {count = 10; void (* BLK) () = ((void) (*) (&); __main_block_impl_0 ((void) __main_block_func_0, & __main_block_desc_0_DATA, count); (void) ((*) (__block_impl (*)) (* __block_impl) BLK) -> (FuncPtr) (__block_impl * BLK));}

Remove complex type conversion, can be simplified as:

Int main (int) {count = 10; sturct = __main_block_impl_0 *blk & __main_block_impl_0 (__main_block_func_0, & / / function pointer; __main_block_desc_0_DATA; //Block)) size, version information (*blk-> FuncPtr) (BLK); / / FuncPtr function calls to BLK, and will own as parameters}

Thus, we can see that Block is also the object in Objective-C. There are three types of
Block (i.e. __block_impl isa pointer to the value of ISA ISA and runtime Objective-C “reference pointer mechanism”), according to the Block object is created by the data area and the difference between different:

  • _NSConcreteStackBlock: the Block object created on the stack
  • _NSConcreteMallocBlock: the Block object created on the heap
  • _NSConcreteGlobalBlock: Block object for the global data area

How to intercept automatic variables

In the upper part of the structure of the Block, and the call mechanism as an anonymous function, when the automatic variable interception occurs at what time?
observation on the __main_block_impl_0 structure in the code body (Block main stack) the constructor can see the variables on the stack count in the form of parameters passed to the constructor, this is the automatic acquisition of variables.
so you can understand this: __block_impl structure already can represent a Block class, but on the stack and declared the __main_block_impl_0 structure, __block_impl package after said on the stack class Block, is to use the Block statement to obtain variables on the stack (the stack is not used in Block variables not captured), variables are stored in the Block structure instance.
so before the BLK (execution), the count of the simple data types on the stack, regardless of what happens, will not affect the value of the Block in the form of parameters to be passed. However, when this variable is a pointer to an object, it is possible to modify the properties of the object.

Block storage domain

As mentioned above, there are three types of Block based on the location of the Block, and the created Block objects are stored in the stack, heap, global data area.

Void (^blk) (void) (NSLog = ^{@ "Global Block");}; int (main) {BLK (NSLog); (@ "% @ [blk", class]); / / print: __NSGlobalBlock__}

As the global BLK above code block is naturally stored in the global data area, but the attention created in the function stack on the BLK, if not intercepted automatic variables, structure instance of Block will be set in the global data area program, rather than on the stack:

Int (main) {void (^blk) (void) = Block ^{// did not intercept the automatic variables (@ NSLog "Stack Block");}; (BLK); NSLog (@ "% @", [blk class]); / / print: __NSGlobalBlock__ int i = 1; void (^captureBlk) (void) = ^{// intercept Block NSLog automatic variable I (@ "Capture:%d", I);}; (captureBlk); NSLog (@ "% @", [captureBlk class]); / / print: __NSMallocBlock__}

You can see that the Block class that intercepted the automatic variable is NSGlobalBlock, which is stored in the global data area.
but why is it that the Block class that captures the NSStackBlock print of an automatic variable is a NSMallocBlock that is set on the heap, rather than on the stack?.

Block replication

The Block configuration on the stack, if the stack scope which belong to the end, the Block will be abandoned, the use of Block still need for Block beyond the scope of Block, provides the Block from the stack is copied to the heap on the method to solve this problem, even if the Block stack scope has ended, but is copied to the Block heap can continue to exist.
copied to the heap on the Block, the _NSConcreteMallocBlock class object written to the Block structure instance of the member variable isa:

Impl.isa = & _NSConcreteMallocBlock;

When ARC is effective, most of the time the compiler will make the decision to automatically generate the Block copy from the stack to the heap on the code, the following situations on the stack Block will automatically copy to the heap:

  • Calling the copy method of Block
  • When Block is returned as a function
  • When Block is assigned to the __strong modified variable
  • When you pass the Block argument to the Cocoa framework containing the usingBlock method or GCD’s API

When you pass the Block to the parameters of the method, you need to manually invoke the copy method to copy the Block. The
section on the stack intercepted the variable I Block is automatically created in the stack, it is NSMallocBlock_, because the Block object is assigned to the _strong of the variable captureBlk (strong is the default modifier ARC object).
because of the above four rules, in ARC actually rarely see _NSConcreteStackBlock class of Block, most of the compiler ensures that the Block is created on the heap, as shown in the following code, only the last line of code directly using a not assigned to the variable Block, its class is __NSStackBlock:

Int count = 0; blk_t = BLK ^ () {NSLog (@ In Stack:%d ", count);}; NSLog (" blk's Class:%@ ", [blk @ class]); / / print: blk's Class:__NSMallocBlock__ NSLog (" Global Block:%@ ", [^{NSLog @ (@" Global Block ");} / / class]); print: Global Block:__NSGlobalBlock__ NSLog (" Copy Block:%@ ", [[^{NSLog @ (@" Copy Block:%d ", count);} copy] class]); / / print: Copy Block:__NSMallocBlock__ NSLog (" Stack Block:%@ ", [^{NSLog @ (@" Stack Block:%d ", count);} class]); / / print: Stack Block:__NSStackBlock__

ARC on the MRC and Block under the difference between the automatic copy, view Block quiz in a few questions will be able to distinguish between the.
in addition, the original book ARC and MRC mixed explanation, distinguish unknown, such as the use of several books on the stack object in Crash example is MRC conditions will occur, but did not do special instructions.

What happened with __block

Automatic variable Block capture add __block specifier, can read and write the variables in Block, can read and write to the variables in the original stack.
automatic variable interception to ensure that the automatic variables on the stack is destroyed, the Block can still use the variable.
__block ensures that the stack and Block (usually on the heap) can access and modify the same variable, __block is how to achieve this function?

__block acts on the principle: the stack on the __block modified with automatic variables into a structure, so that it was created on the heap, so as to facilitate access to the stack or heap and modify the same data.

Validation process:
now on just the code segment, plus the __block specifier, read and write count variables in block and.

Int main (__block int) {count = 10; void (BLK) ^ ^ () = () {count = 20; NSLog (@ In Block:%d ", count); / / print: In Block:20}; count + +; NSLog (@ Out Block:%d", count); / / print: Out Block:11 (BLK);}

The above code segment clang, found that the structure of the Block __main_block_impl_0 structure as follows:

Struct __main_block_impl_0 struct __block_impl struct __main_block_desc_0* {impl; Desc; __Block_byref_count_0 *count; / / by ref __main_block_impl_0 (void *fp, struct __main_block_desc_0 *desc, __Block_byref_count_0 *_count, int flags=0): count (_count-> __forwarding) {impl.isa = & _NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = FP; Desc = DESC;}};

The biggest change is that the count variable is no longer a int type, and count becomes a pointer to the __Block_byref_count_0 structure, the __Block_byref_count_0 structure is as follows:

Struct __Block_byref_count_0 {void *__isa; __Block_byref_count_0 *__forwarding; int __flags; int __size; int count;};

It saves the int count variable, and a pointer to the __Block_byref_count_0 instance of __forwarding, through the following two code __forwarding pointers can know the pointer actually point to the object itself:

The implementation of //Block void __main_block_func_0 (struct __main_block_impl_0 function static *__cself) {__Block_byref_count_0 *count = __cself-> count; / / bound by ref (count-> __forwarding-> count) = 20; / / corresponding to count = 20; NSLog ((NSString) & __NSConstantStringImpl__var_folders_64_vf2p_jz52yz7x4xtcx55yv0r0000gn_T_main_fafeeb_mi_0 (count-> __forwarding-> count));}
The //main function int main () {__attribute__ ((__blocks__ (byref))) {count = __Block_byref_count_0 (void*) 0 (__Block_byref_count_0 * &); count, 0, sizeof (__Block_byref_count_0, 10}); void (BLK) = ((*) (void (*) (&) (__main_block_impl_0 (); void * __main_block_func_0, &); __main_block_desc_0_DATA (__Block_byref_count_0 * &); count, 570425344) (count.__forwarding-> count); / / count + + + +); corresponding; NSLog ((NSString) & __NSConstantStringImpl__var_folders_64_vf2p_jz52yz7x4xtcx55yv0r0000gn_T_main_fafeeb_mi_1 (count.__forwarding-> count); (void) ((*) (__block_impl (*) (__block_impl) *) BLK ->); (FuncPtr) (__block_impl * BLK));}

Why do you want to read and write the count variable through the __forwarding pointer?
in order to ensure either on the stack or on the heap, can pass the __forwarding pointer to find are created on the heap count this main_block_func_0 structure, with the completion of the count-> count (the first count is main_block_func_0, the second count is a int type variable to access and modify). The schematic diagram of
is as follows:

IOS Block usage and implementation principles

my blog site: