preface
Underlying structure of block under Clang analysis
Clang, look at the block
code
int age = 30; void (^yang)(void) = ^{ NSLog(@"%d", age); }; yang();
clang
// The existence of isa indicates that block is an object struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; struct __main_block_desc_0 { size_t reserved; size_t Block_size; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; // It can capture the effect of external variables and functions int age; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_zm_558cwfjs099fbm2r8kxg8wt00000gt_T_main_bfedde_mi_0, age); } 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[]) { NSString * appDelegateClassName; /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; int age = 30; // Pointing to an address indicates that block is a pointer void (*yang)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); ((void (*)(__block_impl *))((__block_impl *)yang)->FuncPtr)((__block_impl *)yang); appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))); } return UIApplicationMain(argc, argv, __null, appDelegateClassName); }
block generated structure
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; };
Implementation of block
void (*yang)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
Anonymous function of block
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_zm_558cwfjs099fbm2r8kxg8wt00000gt_T_main_bfedde_mi_0, age); }
Call of block
((void (*)(__block_impl *))((__block_impl *)yang)->FuncPtr)((__block_impl *)yang);
analysis
- The existence of isa indicates that block is an object
- It can capture the effect of external variables and functions
- Pointing to an address indicates that block is a pointer
Complete analysis borrow a network diagram
LLDB debug block
copy the structure compiled by Clang to our mian file, connect a structure variable zyblock, and debug the internal of the block structure through lldb
main code
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; struct __main_block_desc_0 { size_t reserved; size_t Block_size; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; }; int main(int argc, char * argv[]) { NSString * appDelegateClassName; @autoreleasepool { int age = 30; void (^yang)(void) = ^{ NSLog(@"%d", age); }; struct __main_block_impl_0 *zyblock = (__bridge struct __main_block_impl_0 *)yang; // breakpoint yang(); appDelegateClassName = NSStringFromClass([AppDelegate class]); } return UIApplicationMain(argc, argv, nil, appDelegateClassName); }
Breakpoint I
Breakpoint II
Open assembly
analysis
Internal Hierarchy
The address of the first line of the code inside the block braces is the same as the address pointed to by the FuncPtr pointer
problem
How is the code in block braces stored?
What does it have to do with the FuncPtr pointer?
Let's look down with questions
Source code and cpp code comparison
1. External variables captured by block
2. Defined block yang
3. Call block
Generate block ^ {}
// =The ^ {} on the right is compiled into ((void (*) ()) &_main_block_impl_0 ((void *) _main_block_func_0, &_main_block_desc_0_data, age)) void (^yang)(void) = ^{ NSLog(@"%d",age);}; void (*yang)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); // Talk about yang pointing__ main_block_impl_0 structure address
analysis
- =The ^ {} on the right is compiled into ((void (*) ()) &_main_block_impl_0 ((void *) _main_block_func_0, &_main_block_desc_0_data, age))
- Point yang to__ main_block_impl_0 structure address
- Passed__ main_block_func_0 __main_block_desc_0_DATA age 3 parameters
- Strong transpose address ((void (*) ()) type
- Assign address to yang pointer
__main_block_impl_0
In c + +, if struct has a custom constructor and the constructor is followed by list initialization, it can only be written to the implementation
definition
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; };
realization
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; // Custom constructor: this symbol indicates that the initialization list is followed // It is to give the value passed in from the outside to _agefirst, and then age = _age. These belong to c + + syntax __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
__main_block_func_0
// The compilation will put the code of the block block into this function // Function plus static keyword function address fixed function in global area static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_zm_558cwfjs099fbm2r8kxg8wt00000gt_T_main_9da3e9_mi_0, age); }
Parameter transfer is shown in the figure below:
analysis
-
__The main_block_func_0 function stores the code in block braces
This function is directly assigned to the first parameter fp pointer of the _main_block_impl_0 constructor
fp is also assigned to the FuncPtr variable in the _block_impl structure -
The FuncPtr variable points to the _main_block_func_0 function, which is the code inside the block braces
That is, if you write code inside the block, the compiler will create a fun0 function
__main_block_desc_0
static struct __main_block_desc_0 { size_t reserved; size_t Block_size; } __main_block_desc_0_DATA = { , sizeof(struct __main_block_impl_0)};
Parameter transfer is shown in the figure below:
analysis
A structure has two member variables. At the same time, a structure variable _main_block_desc_0_DATA is defined and assigned. reserved is assigned as 0, and Block_size is assigned as sizeof (struct _main_block_impl_0), that is, the memory size of the structure
block call
__block_impl
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
analysis
- The structure has four member variables. The first member variable is the isa pointer. We know that this is the exclusive flag of the oc object (instance, class and metaclass). Because _block_impl is inside _main_block_impl_0, _main_block_impl_0 is an oc object, and the essence of the oc object is to be a structure in memory
- yang is a _main_block_impl_0 struct pointer
- yang is a structure pointer, so the access member FuncPtr uses - >
problem
1. FuncPtr is not a member variable of _main_block_impl_0 structure. It is a member variable of _block_impl. Shouldn't the variable be Yang - > impl.FuncPtr?
2. yang has cast (_block_impl *)yang, and there is a FuncPtr variable in the _block_impl structure, but these are completely two different structures, and the reference cannot be cast?
Parent child forced conversion type
c/c + + often uses parent-child access
If the memory layout and order of the first few members of the parent and the first few members of the child are consistent, you can use the parent to refer to the child
As shown in the figure
__The main_block_impl_0 structure completely coincides with the _block_impl structure within the size range of the _block_impl structure (actually the same piece of memory), but the size of the _main_block_impl_0 structure is larger than that of the _block_impl structure. After forced conversion, the block can directly reference the FuncPtr member variable
Overseas connection structure
Use of OC forced transfer type
Suppose we call the funyang function through a function pointer
code
struct Car { NSString *isa; int price; int year; void *ptr; }car; struct Person { struct Car car; int age; }person; void funyang(struct Car *xmcar) { NSLog(@"1"); }
We can find the funyang function address in the following ways
- (void)test1 { person.car.ptr = (void *)funyang; struct Person *yang = &person; void *p1 = &funyang; void *p2 = person.car.ptr; void *p3 = yang->car.ptr; void *p4 = ((struct Car *)yang)->ptr; }
output
(lldb) p p1 (void *) $0 = 0x000000010bcabd70 (lldb) p p2 (void *) $1 = 0x000000010bcabd70 (lldb) p p3 (void *) $2 = 0x000000010bcabd70 (lldb) p p4 (void *) $3 = 0x000000010bcabd70 (lldb)
Here, in order to help understand the above block, we choose the strong rotation method
// yang itself is a pointer to the Person structure, equivalent to _main_block_impl_0 above // The pointer from yang to Car structure is equivalent to _block_impl above ((struct Car *)yang)->ptr;
use
- (void)test { // Assign values to structure members person.car.isa = @"simulation block"; person.car.price = 20; person.car.year = 2021; // Assign funyang's function address to ptr person.car.ptr = (void *)funyang; person.age = 30; // Next, I access ptr through a pointer struct Person *yang = &person; // Normal access is like this yang->car.ptr; // If I'm forced to switch to Car, this is the case ((struct Car *)yang)->ptr; /* The function we want to access void funyang(struct Car *xmcar) Function type void (*)(struct Car *) Parameter type (struct Car *) */ // Use p4 to point to PTR. PTR is the function pointer, and the address of funyang function is stored void *p4 = ((struct Car *)yang)->ptr; // Strongly convert p4 to a function type ((void (*) (struct Car *))); ((void (*)(struct Car *)) p4); // Call the function and pass in parameters. The parameter type is the parameter type of funyang function ((void (*)(struct Car *)) p4)((struct Car *)yang); // Remove p4 ((void (*)(struct Car *)) ((struct Car *)yang)->ptr)((struct Car *)yang);
We can also visit like this
// We can also access it indirectly again with a structure pointer struct Car *xiaoyang = (struct Car *)yang; ((void (*)(struct Car *))((struct Car *)xiaoyang)->ptr)((struct Car *)yang);
output
2021-11-24 20:40:10.167551+0800 07_essence[61067:32686116] 1 2021-11-24 20:40:10.167699+0800 07_essence[61067:32686116] 1
analysis
ABCD pointer calls once ABCD pointer calls once
If the function parameter to be accessed is of type Person
void funyang(struct Person *xmcar) { NSLog(@"1"); }
call
struct Person *nb = &person; nb->car.ptr = (void *)funyang; ((void (*)(struct Person *)) nb->car.ptr)(nb); ((void (*)(struct Person *)) (nb->car).ptr)(nb); void * p5 = nb->car.ptr; ((void (*)(struct Person *)) p5)((struct Person *) nb); ((void (*)(struct Person *)) nb->car.ptr)((struct Person *) nb);
After understanding the oc example above, it will be much simpler to look at the block code. In fact, it is
code
void (^yang)(void) = ^{}; yang();
Compile generation
void (*yang)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); ((void (*)(__block_impl *))((__block_impl *)yang)->FuncPtr)((__block_impl *)yang);
simplify
void (*yang)(void) = &__main_block_impl_0(Parameter 1, parameter 2, parameter 3) yang->FuncPtr(parameter)
give an example
In Apple's source code, we often use this way to understand the block. It is the same in other places. Let's take the dyld source code as an example
block memory distribution
Figure 1 source code
Figure 2: viewing Block anonymous function addresses
analysis
1. Find p object address
2. Structure _main_block_impl_0 address found
3. Find the address of _funcptrin _main_block_impl_0 structure
Figure 3 shows the size of the block structure and the value of the auto variable int a captured by i
analysis
1. Find p object address
2. p object offset 8 bytes, find structure _main_block_impl_0 address
3. _main_block_impl_0 the first address is also the _block_impl structure address _block_impl, accounting for 24 bytes
4. _main_block_impl_0 the first address offset of 24 bytes is _main_block_desc_0 the structure address _main_block_desc_0 occupies 8 bytes
5. __main_block_desc_0 the first address offset of 4 bytes is Blocksize address blocksize = 24+8+4 = 36
6. _main_block_impl_0 the first address offset of 32 bytes is the address of int a
conclusion
- Block is essentially an oc object, which is stored in memory in the form of block_impl_0 structure
- block itself is also a pointer, which stores the memory address of the structure
- When ^ {} is encountered, the compiler will generate a fun0 function, put the code block we wrote into the fun0 function, and then store the entry address of the fun() function in block_ impl_ Member variable FuncPtr in 0 structure
- FuncPtr Func stands for function prt stands for pointer address PTR point address
- block_impl is a function call structure. When we use block() to call a block, we are actually looking for the func address