Learn the key

Block underlying structure

Block type

Block underlying source code exploration

1. Block captures variables

To create an iOS project, write the following code in the customized BlockTestViewController:

/ / BlockTestViewController. M file # import "BlockTestViewController. H" # import "Person. H" @ interface BlockTestViewController () @property (nonatomic, strong) Person *person; @end @implementation BlockTestViewController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; [self blockTest]; } - (void)blockTest {int a = 18; void(^block_a)(void) = ^{ printf("a - %d",a); }; block_a(); __block int b = 20; void(^block_b)(void) = ^{ b++; printf("b - %d",b); }; block_b(); Person *person1 = [[Person alloc] init]; void(^block_p)(void) = ^{ person1.name = @"222"; NSLog(@"person.name = %@", person1.name); }; block_p(); __block Person *person2 = [[Person alloc] init]; void(^block_p2)(void) = ^{ person2.name = @"333"; NSLog(@"person.name = %@", person2.name); }; block_p2(); __weak Typeof (self) weakSelf = self; void(^block_weak)(void) = ^{ weakSelf.person.name = @"444"; NSLog(@"person.name = %@", weakSelf.person.name); }; block_weak(); Block void(^block_block)(void) = ^{block_weak(); }; block_block(); } - (Person *)person { if (! _person) { _person = [[Person alloc] init]; } return _person; } - (void)dealloc {NSLog(@" destroyed!! ") ); } @end // person.h file @interface Person: NSObject @property (nonatomic, copy) NSString *name; #import "person. h" @implementation person@endCopy the code

In order to study the underlying structure Block, first of all, we will open a terminal use xcrun command BlockTestViewController. M compile files for the CPP file, as shown in the following: command as follows:

Xcrun-sdk iphones \ > clang-arch arm64 \ > -rewrite-objc \ > -fobjc-arc \ > -fobjc-Runtime =macosx-10.14 \ > -isysroot / Applications/Xcode. App/Contents/Developer/Platforms/iPhoneSimulator platform/Developer/SDKs/iPhoneSimulator14.5 SDK \ >  BlockTestViewController.mCopy the code

Looking through the compiled CPP code above, you can see that some of the constructs nested the same __block_impl structure and other data structure variables, and used some external functions, as follows:

Extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32]; extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32]; struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; // extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int); // Extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int); extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int);Copy the code

Emsp: if you are not familiar with the C++ language, the above code may be a struggle to look at, so in the course of further exploration, the above compiled CPP code will be abbreviated (for example: __BlockTestViewController__blockTest_block_impl_0 is written as _block_IMPL_0. We can see that these structures are named in a regular way, prefixed by their definition location (filename __ function name).

1.1 Block Captures local variables

The relevant CPP code generated by the first set of blocks looks like this:

static struct _block_desc_0 { size_t reserved; size_t Block_size; } _block_desc_0_DATA = { 0, sizeof(struct _block_impl_0)}; struct _block_impl_0 { struct __block_impl impl; struct _block_desc_0* Desc; int a; _block_impl_0(void *fp, struct _block_desc_0 *desc, int _A, int flags=0) a(_a) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void _block_func_0(struct _block_impl_0 *__cself) { int a = __cself->a; // bound by copy printf("a - %d",a); }Copy the code

The relevant CPP code compiled by block_A in the blockTest method is shown below (I have modified the CPP code to make it easier for you to read, but it does not affect the execution result) :

int a = 18; void(*block_a)(void); // The _block_impl_0 constructor is called to initialize the block_impl_0 variable, and the _block_func_0 function pointer is cast to a void * null pointer as an argument, Struct _block_IMPL_0 block_IMPL_0 {(void *) _block_func_0, &_block_desc_0_DATA, a}; // Get a pointer to the block_impl_0 variable and force this pointer to a function of type void (*)(). block_a = ( void (*)() ) &block_impl_0; Function FuncPtr (void (*)(struct __block_impl *)); void (*)(struct __block_impl *) Finally, block_A is passed in as a __block_impl structure pointer. ( (void (*)(struct __block_impl *) ) ((struct __block_impl *)block_a)->FuncPtr)( (__block_impl *) block_a);Copy the code

By analyzing the above code, we can see that the Block type variable block_A defined in OC compiles into a special function _block_func_0 and generates a structure type _block_IMPL_0. Block_a is actually a struct type variable that contains all member variables in the __block_impl structure. When initialized, block_A is initialized as a pointer to _NSConcreteStackBlock. Flags is initialized to 0, FuncPtr is initialized to the code block function pointer _block_func_0), and the local variable a of the basic data type is captured by copying its value into a member variable A of the same type as the local variable A. When the code Block in the OC is then executed, underneath it is actually a call to the function pointer FuncPtr, a member of the generated block_A struct variable, and a pointer to the block_A struct variable is passed in as an argument so that in the _block_func_0 function, You can use the value of the captured local variable A.

1.2 Block Captures local variables decorated with __block

The related CPP code generated by the second set of blocks looks like this:

static void _block_copy_1(struct _block_impl_1*dst, struct _block_impl_1*src) { _Block_object_assign((void*)&dst->b, (void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/); } static void _block_dispose_1(struct _block_impl_1*src) { _Block_object_dispose((void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/); } static struct _block_desc_1 { size_t reserved; size_t Block_size; void (*copy)(struct _block_impl_1*, struct _block_impl_1*); void (*dispose)(struct _block_impl_1*); } _block_desc_1_DATA = { 0, sizeof(struct _block_impl_1), _block_copy_1, _block_dispose_1}; struct __Block_byref_b_0 { void *__isa; __Block_byref_b_0 *__forwarding; int __flags; int __size; int b; }; struct _block_impl_1 { struct __block_impl impl; struct _block_desc_1* Desc; __Block_byref_b_0 *b; // by ref _block_impl_1(void *fp, struct _block_desc_1 *desc, __Block_byref_b_0 *_b, int flags=0) : b(_b->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void _block_func_1(struct _block_impl_1 *__cself) { __Block_byref_b_0 *b = __cself->b; // bound by ref (b->__forwarding->b)++; printf("b - %d",(b->__forwarding->b)); }Copy the code

The relevant CPP code compiled by block_B in the blockTest method is as follows:

    ((void (*)(__block_impl *))((__block_impl *)block_b)->FuncPtr)((__block_impl *)block_b);
    
       __attribute__((__blocks__(byref))) struct __Block_byref_b_0 b = { (void *) 0, (__Block_byref_b_0 *) &b, 0, sizeof(__Block_byref_b_0), 20};
    
    void(*block_b)(void);
    
    struct _block_impl_1 block_impl_1{(void *)_block_func_1, &_block_desc_1_DATA, (__Block_byref_b_0 *) &b, 570425344};
    
    block_b = (void (*)(void))&block_impl_1;
    
    ( (void(*)(struct __block_impl *) ) ( ( (struct __block_impl *) block_b)->FuncPtr ) ) ( (struct __block_impl *) block_b);
Copy the code

After analysis, the definition and use of Block_B are similar to that of block_A, but differ in two areas:

  • 1: When the compiler compiles to this line of code, the __Block_byref_b_0 structure type is defined, and B is no longer a normal data type variable, but a __Block_byref_b_0 structure variable. When this variable is initialized, isa is set to ((void *) 0) NULL, __forwarding is set to a pointer to B, __flags is set to 0, __size is set to its structure size, The member variable B is initialized to 20 (the value of the local variable b originally defined in OC), and after the __Block_byref_b_0 structure variable B is initialized, its address pointer is assigned to the member variable (__Block_byref_b_0 * type) B of Block_IMPL_1.

  • 2: Block_b member variable Desc Pointer type _block_DESc_1 has two more function pointer type member variables copy and dispose, and global variable _block_desc_1_DATA is initialized. The values of these two member variables are the _block_copy_1 and the _block_dispose_1 function pointer.

Back in the OC code, the __block modifier is used before the local variable b, because we changed the value of B in the block of the block_B closure, so we need to capture the address pointer of the local variable B, and use __block to modify the function of the local variable of the basic data type. In the compiler compilation phase, a __Block_byref_b_0 structure containing the basic data type b member variable B is generated, and a variable B of __Block_byref_b_0 structure type is created. The __Block_byref_b_0 structure pointer b is also defined in the closure structure variable block_B. When initializing block_B, the address of the __Block_byref_b_0 structure variable B is passed. This allows the closure block to modify and access the data in the memory space B to which the pointer to its member variable B points (you can see that the compiler wraps the __block modifier local variable B as an OC object, since __Block_byref_b_0 has ISA).

1.3 Block Captures OC objects

The related CPP code generated by the third set of blocks is as follows:

static void _block_copy_2(struct _block_impl_2*dst, struct _block_impl_2*src) { _Block_object_assign((void*)&dst->person1, (void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/); } static void _block_dispose_2(struct _block_impl_2*src) { _Block_object_dispose((void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/); } static struct _block_desc_2 { size_t reserved; size_t Block_size; void (*copy)(struct _block_impl_2*, struct _block_impl_2*); void (*dispose)(struct _block_impl_2*); } _block_desc_2_DATA = { 0, sizeof(struct _block_impl_2), _block_copy_2, _block_dispose_2}; struct _block_impl_2 { struct __block_impl impl; struct _block_desc_2* Desc; Person *__strong person1; _block_impl_2(void *fp, struct _block_desc_2 *desc, Person *__strong _person1, int flags=0) : person1(_person1) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void _block_func_2(struct _block_impl_2 *__cself) { Person *__strong person1 = __cself->person1; // bound by copy ((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)person1, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_0); NSLog((NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_1, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)person1, sel_registerName("name"))); }Copy the code

The CPP code for block_p compilation in the blockTest method is as follows:

// Force objc_msgSend to null and then force (Person * (*) (id, SEL)) to call... Person *person1 = ( (Person * (*) (id, SEL) ) (void *) objc_msgSend ) ((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init")); void(*block_p)(void); struct _block_impl_2 block_impl_2{(void *)_block_func_2, &_block_desc_2_DATA, person1, 570425344}; block_p = (void (*)(void))&block_impl_2; ((void (*)(__block_impl *))((__block_impl *)block_p)->FuncPtr)((__block_impl *)block_p);Copy the code

When the closure captures OC objects, it does not generate a __Block_byref_p_0 struct type at compile time as the second Block does. However, copy and dispose function pointer are passed when global variable _block_desc_2_DATA is defined.

1.4 Block Captures OC objects decorated with __block

The relevant CPP code generated by the fourth block is as follows:

static void _block_copy_3(struct _block_impl_3*dst, struct _block_impl_3*src) {

    _Block_object_assign((void*)&dst->person2, (void*)src->person2, 8/*BLOCK_FIELD_IS_BYREF*/);
    
}


static void _block_dispose_3(struct _block_impl_3*src) {_Block_object_dispose((void*)src->person2, 8/*BLOCK_FIELD_IS_BYREF*/);}


static struct _block_desc_3 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct _block_impl_3*, struct _block_impl_3*);
  void (*dispose)(struct _block_impl_3*);
} _block_desc_3_DATA = { 0, sizeof(struct _block_impl_3), _block_copy_3, _block_dispose_3};


struct __Block_byref_person2_1 {
  void *__isa;
__Block_byref_person2_1 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *__strong person2;
};


struct _block_impl_3 {
  struct __block_impl impl;
  struct _block_desc_3* Desc;
  __Block_byref_person2_1 *person2; // by ref
  _block_impl_3(void *fp, struct _block_desc_3 *desc, __Block_byref_person2_1 *_person2, int flags=0) : person2(_person2->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};


static void __Block_byref_id_object_copy_131(void *dst, void *src) {

    _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
    
}


static void __Block_byref_id_object_dispose_131(void *src) {

    _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
    
}

static void _block_func_3(struct _block_impl_3 *__cself) {
    __Block_byref_person2_1 *person2 = __cself->person2; // bound by ref

    ((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)(person2->__forwarding->person2), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_2);
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_3, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)(person2->__forwarding->person2), sel_registerName("name")));
        
}

Copy the code

The CPP code for block_p2 compilation in the blockTest method looks like this:

 __attribute__((__blocks__(byref))) __Block_byref_person2_1 person2 = {(void*)0,(__Block_byref_person2_1 *)&person2, 33554432, sizeof(__Block_byref_person2_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"))};
 
    void(*block_p2)(void);
    
    struct _block_impl_3 block_impl_3{(void *)_block_func_3, &_block_desc_3_DATA, person2, 570425344};

    block_p2 = (void (*)(void))&block_impl_3;

    ((void (*)(__block_impl *))((__block_impl *)block_p2)->FuncPtr)((__block_impl *)block_p2);
Copy the code

Analysis shows that the Block captures OC objects with __block modifier, similar to the second Block, except that the second Block stores a basic data type B in a member variable B of the __Block_byref_b_0 structure type variable. Then save the __Block_byref_b_0 variable pointer to the member variable B of the _block_IMPL_1 structure variable block_IMPL_1, This group stores an OC object into the member variable person2 in the __Block_byref_person2_1 structure. The __Block_byref_person2_1 variable pointer is then saved into the member variable person2 of the _block_IMPL_3 structure variable block_IMPL_3.

1.5 Block captures OC objects decorated with __weak

The relevant CPP code generated by the fifth block is as follows:

static struct _block_desc_4 { size_t reserved; size_t Block_size; void (*copy)(struct _block_impl_4*, struct _block_impl_4*); void (*dispose)(struct _block_impl_4*); } _block_desc_4_DATA = { 0, sizeof(struct _block_impl_4), _block_copy_4, _block_dispose_4}; static void _block_copy_4(struct _block_impl_4*dst, struct _block_impl_4*src) { _Block_object_assign((void*)&dst->weakSelf, (void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/); } static void _block_dispose_4(struct _block_impl_4*src) { _Block_object_dispose((void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/); } struct _block_impl_4 { struct __block_impl impl; struct _block_desc_4* Desc; BlockTestViewController *const __weak weakSelf; _block_impl_4(void *fp, struct _block_desc_4 *desc, BlockTestViewController *const __weak _weakSelf, int flags=0) : weakSelf(_weakSelf) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void _block_func_4(struct _block_impl_4 *__cself) { BlockTestViewController *const __weak weakSelf = __cself->weakSelf; // bound by copy ((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)weakSelf, sel_registerName("person")), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_4); NSLog((NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_5, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)weakSelf, sel_registerName("person")), sel_registerName("name"))); }Copy the code

The relevant CPP code for block_weak compilation in the blockTest method is as follows:

    __attribute__((objc_ownership(weak))) typeof(self) weakSelf = self;

    void(*block_weak)(void);
    
    struct _block_impl_4 block_impl_4{(void *)_block_func_4, &_block_desc_4_DATA, weakSelf, 570425344};
    
    block_weak = (void (*)(void))&block_impl_4;

    ((void (*)(__block_impl *))((__block_impl *)block_weak)->FuncPtr)((__block_impl *)block_weak);
Copy the code

Analysis shows that the only difference between a Block capturing an OC object with an __weak modifier and a normal OC object is that the block_impl uses the __weak keyword to modify the captured member variables and the variables defined

1.6 Block Captures Block variables

The related CPP code generated by the sixth block is as follows:

struct _block_impl_5 { struct __block_impl impl; struct _block_desc_5* Desc; struct __block_impl *block_weak; _block_impl_5(void *fp, struct _block_desc_5 *desc, void *_block_weak, int flags=0) : block_weak((struct __block_impl *)_block_weak) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void _block_func_5(struct _block_impl_5 *__cself) { void (*block_weak__strong)() = (void (*__strong)())__cself->block_weak; // bound by copy ((void (*)(__block_impl *))((__block_impl *)block_weak)->FuncPtr)((__block_impl *)block_weak); } static void _block_copy_5(struct _block_impl_5*dst, struct _block_impl_5*src) { _Block_object_assign((void*)&dst->block_weak, (void*)src->block_weak, 7/*BLOCK_FIELD_IS_BLOCK*/); } static void _block_dispose_5(struct _block_impl_5*src) { _Block_object_dispose((void*)src->block_weak, 7/*BLOCK_FIELD_IS_BLOCK*/); } static struct _block_desc_5 { size_t reserved; size_t Block_size; void (*copy)(struct _block_impl_5*, struct _block_impl_5*); void (*dispose)(struct _block_impl_5*); } _block_desc_5_DATA = { 0, sizeof(struct _block_impl_5), _block_copy_5, _block_dispose_5};Copy the code

The CPP code for block_block compilation in the blockTest method looks like this:

    void(*block_block)(void);
    
    struct _block_impl_5 block_impl_5{(void *)_block_func_5, &_block_desc_5_DATA, block_weak, 570425344};
    
    block_block = (void (*)(void))&block_impl_5;

    ((void (*)(__block_impl *))((__block_impl *)block_block)->FuncPtr)((__block_impl *)block_block);
Copy the code

Analysis shows that a Block captures a Block object and only assigns the captured Block object to its member variable block_weak.

1.7 Block captures variable summary

When a Block is defined, the compiler generates a structure for that Block (regardless of whether a Block of the same type as the current Block has been compiled before). If the captured variable (whether a block, a primitive data type, or an OC object) is modified by the __block keyword, then a separate compilation generates a structure named __Block_Byref_ that captures the variable’s name, And copy the value of the local variable modified by the __block keyword (Block objects and OC objects are pointer values) into the corresponding member variable. Then the __Block_impl_block name type structure defines a member variable of this type pointer. If the captured variable is __weak, the local variable is modified with __attribute__((objc_ownership(weak))), And the local variable is either copied to the __Block_impl_block name type structure or the corresponding member variable in the __Block_Byref_ capture variable name structure is __weak. If there is no keyword to modify the local variable, The __Block_impl_block name type structure defines a local variable of the same type as this local variable.

2. Block type

After the static analysis of the Block underlying structure, I have a certain familiarity with and understanding of the Block, but at runtime, what is the situation? We don’t know yet, so let’s analyze the Block code dynamically.

First, in BlockTestViewController. M file add the following blockTypeTest method, and the calling in the viewDidLoad method.

// Define global variable NSString *globalStr = @" global variable "; - (void)blockTypeTest {//1. Block int b = 20; void(^block_b)(void) = ^{ NSLog(@"b - %d",b); }; block_b(); Void (^block_c)(void) = ^{NSLog(@"globalStr - %@", globalStr); }; block_c(); //3. __weak block a = 18; void(__weak ^block_a)(void) = ^{ NSLog(@"a - %d",a); }; block_a(); }Copy the code

Breakpoints are made at each set of Block initializations to compile and run the program

2.1 the heap Block

When the program is executed at the initialization of Block_B, the assembly code is displayed, as shown below:

The objc_retainBlock function is also called when the create Block object is initialized.

Add a symbolic breakpoint for the objc_retainBlock function. After the breakpoint is reached, _Block_copy is called from the objc_retainBlock function, as shown in the following figure:

Add a symbolic breakpoint for the _Block_copy function, go to the symbolic breakpoint and see the following assembly code:

_Block_copy is a function that belongs to the libsystem_blocks library. The libsystem_blocks library is not open source, but let’s look at what _Block_copy does. First look at register x0 data, as shown below:

Then break the breakpoint before the _Block_copy function returns, and look at the return value, as shown below:

Block __NSStackBlock__ has been changed from __NSMallocBlock to __NSMallocBlock. The address is also different. The signature value of block_A is its Block signature. Wrap this type as an NSMethodSignature object and print its information, as shown below:

2.2 global Block

When the program is executed at the initialization of block_C, the assembly code is displayed, as shown below:

The objc_retainBlock function is also called, the breakpoint is dropped, and the program is executed to _Block_copy to view the parameters passed in, as shown below:

Then break the breakpoint before the _Block_copy function returns, and look at the return value, as shown below:

You can see that block_c is an __NSGlobalBlock__ Block before and after the _Block_copy function is called, and the address does not change. Therefore, __NSGlobalBlock__ blocks, like global variables, are initialized at runtime.

2.3 stack Block

When the program is executed at the definition of Block_A, the assembly code is displayed, as shown below:

Objc_initWeak, objc_loadWeakRetained, objc_Release, and objc_destroyWeak are called, but objc_retainBlock is not called as the other two blocks. So a Block object with an __weak modifier is still a Block of type __NSStackBlock__.

3. Explore the underlying source code of Block

So far, we are familiar with the underlying Block data structure, but if you want to know what the _Block_copy function does, you need to explore the libsystem_blocks library, which is not open source, so we have to go a little further. To see the code in libclosure, another open source library, you can download it with the password z0wf.

3.1 exploration of _Block_copy function and related structures

Once the download is complete, search the _Block_copy keyword and find the following code:

Based on the analysis of the above code, let’s take a look at the Block_layout structure, as shown below:

The struct Block_descriptor_1 * descriptor descriptor is defined as follows: struct Block_descriptor_1 * descriptor descriptor

You can see that the Block_descriptor_1, Block_descriptor_2, and Block_descriptor_3 are defined here, very similar to the underlying structure of the source code that we have seen. But why don’t we see the Block_descriptor_2 and the pointer to the Block_descriptor_3 structure defined in Block_layout? How do you get the copy and dispose pointer to Block_descriptor_2? With this in doubt search for the Block_descriptor_3 keyword in the source code to find the following code:

By analyzing the above code, we can get the memory structure diagram of the Block_layout structure variable, as shown below:

In addition to the above struct types, the other struct types we see in the source code are Block_byref, which is defined as follows:

3.1 Explore the copy function

After analyzing the above code and structure types, let’s focus on the _Block_call_copy_helper function called from Block_copy, which looks like this:

In this function, the inline function _Block_get_copy_function is called, with the code shown below:

The _Block_get_copy_fn function code is as follows:

After the above analysis, we can know that _Block_call_copy_helper is used to get the copy function in the Block and call it. The arguments passed in are result (a pointer to the Block_layout type of the heap) and aBlock (a pointer to the Block_layout type of the stack). The copy function called the _Block_object_assign function in the source code we were looking at earlier. The source code is as follows:

In this function, the variables are handled separately according to the type of the captured variable. If the captured type is an OC object, its reference count is incremented, and if the captured variable is a Block object, Block_copy is called to operate on the Block object. If the captured variable type is an __weak or __block modified OC object or a Block wrapper type Byref object, the _Block_byref_copy function is called to operate on the Byref object. The _Block_byref_copy function code is shown below:

In this function, the src2 pointer member variable byref_keep is called, and in the previous source (1.4 Block to capture OC objects decorated with __block), this byref_keep was assigned to the following function:

Select person2 from the __Block_byref_person2_1 structure variable person2, as shown below:

The _Block_object_assign function is then called to increment the reference count of the OC object person2.

To be continued….