Data structure of block

Keywords: iOS data structure objective-c

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

Posted by Onloac on Fri, 03 Dec 2021 17:35:38 -0800