Goal:

  1. Familiar with Block basic principles
  2. Familiar with memory copy

How do you learn the concepts in depth? Convert the OC code to C++ code to see its internal implementation. During the interview process, each question has a corresponding score, be sure to answer as well as possible.

What is a Block

Often meet try:

  1. What is the Block?
  2. What do you make of the call to Block?
  3. Intercepting variables is a feature of blocks. How does the system intercept blocks?
  4. What do we do with the __block modifier?
  5. Do stack blocks and heap blocks understand when a Block needs to be copied?

6. Circular references to blocks

1. What is Block?

A Block is an object that encapsulates the execution function and context.

The source code parsing

+ (void)testBlock1{
    int multiplier = 6;
    int (^Block)(int) = ^int(int num) {
        return num *multiplier;
    };
    Block(2);

}
Copy the code

Use clang-rewrite-objc file.m to view the contents of the compiled file

struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; struct __TestObject__testBlock1_block_impl_0 { struct __block_impl impl; struct __TestObject__testBlock1_block_desc_0* Desc; int multiplier; __TestObject__testBlock1_block_impl_0(void *fp, struct __TestObject__testBlock1_block_desc_0 *desc, int _multiplier, int flags=0) : multiplier(_multiplier) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static int __TestObject__testBlock1_block_func_0(struct __TestObject__testBlock1_block_impl_0 *__cself, int num) { int multiplier = __cself->multiplier; // bound by copy return num *multiplier; } static struct __TestObject__testBlock1_block_desc_0 { size_t reserved; size_t Block_size; } __TestObject__testBlock1_block_desc_0_DATA = { 0, sizeof(struct __TestObject__testBlock1_block_impl_0)}; // _C stands for class method, _I instance method, TstObject stands for class name, Static void _C_TestObject_testBlock1(Class self, SEL _cmd) {int multiplier = 6; int (*Block)(int) = ((int (*)(int))&__TestObject__testBlock1_block_impl_0((void *)__TestObject__testBlock1_block_func_0,  &__TestObject__testBlock1_block_desc_0_DATA, multiplier)); ((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, 2); }Copy the code

A Block call is a function call. Fetching a function pointer from a Block passes two arguments, Block, and the required arguments.

Intercept variable

  • Intercepts values for local variables of primitive data types.
  • Local variables of object types are intercepted (strong references) along with ownership modifiers.
  • Intercepts local static variables in pointer form.
  • Do not intercept global variables, static global variables.

use

- (void)method {
    // Local variable of primitive type
    int var =1;
    // A local variable of the object type
    __unsafe_unretained id unsafe_obj = nil;
    __strong id strong_obj = nil;
    
    Static local variables
    static int static_var = 3;
    
    void(^Block)(void) = ^{
        NSLog(@"Local variable < primitive data type > var %d".var);
        NSLog(@"Local variable <__unsafe_unretained object type > var %@",unsafe_obj);
        NSLog(@Local variable <__strong object type > var %@,strong_obj);
        
        NSLog(@"Static variable %d",static_var);
        
        NSLog(@"Global variable < primitive data type > var %d",global_var);
        NSLog(@"Static global variable < primitive data type > var %d",static_global_var);
        
        // Using the object type will prevent circular references to prompt
// NSLog(@"TestObject object type member num %d",self.num);
    };
    Block();
    
}


Copy the code

clang -rewrite-objc -fobjc-arc file.m

struct __TestObject__method_block_impl_0 {
  struct __block_impl impl;
  struct __TestObject__method_block_desc_0* Desc;
  int var;
  __unsafe_unretained id unsafe_obj;
  __strong id strong_obj;
  int *static_var;
  __TestObject__method_block_impl_0(void *fp, struct __TestObject__method_block_desc_0 *desc, int _var, __unsafe_unretained id _unsafe_obj, __strong id _strong_obj, int *_static_var, int flags=0) : var(_var), unsafe_obj(_unsafe_obj), strong_obj(_strong_obj), static_var(_static_var){ impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __TestObject__method_block_func_0(struct __TestObject__method_block_impl_0 *__cself) {
  int var = __cself->var; // bound by copy
  __unsafe_unretained id unsafe_obj = __cself->unsafe_obj; // bound by copy
  __strong id strong_obj = __cself->strong_obj; // bound by copy
  int *static_var = __cself->static_var; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_0,var);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_1,unsafe_obj);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_2,strong_obj);

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_3,(*static_var));

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_4,global_var);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_5,static_global_var);



    }
Copy the code

__block qualifier

Usage Scenario: In general, the __bloc modifier is required to perform an assignment to an intercepted variable.

Variables decorated with __block become objects.

+ (void)testBlock1{
    __block int multiplier = 6;
    int (^Block)(int) = ^int(int num) {
        return num *multiplier;
    };
    Block(2);

}

// compiled into a C++ file
struct __Block_byref_multiplier_0 {
  void *__isa;
__Block_byref_multiplier_0 *__forwarding;
 int __flags;
 int __size;
 int multiplier;
};

struct __TestObject__testBlock1_block_impl_0 {
  struct __block_impl impl;
  struct __TestObject__testBlock1_block_desc_0* Desc;
  __Block_byref_multiplier_0 *multiplier; // by ref
  __TestObject__testBlock1_block_impl_0(void *fp, struct __TestObject__testBlock1_block_desc_0 *desc, __Block_byref_multiplier_0 *_multiplier, int flags=0) : multiplier(_multiplier->__forwarding){ impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void _C_TestObject_testBlock1(Class self, SEL _cmd) {
    __attribute__((__blocks__(byref))) __Block_byref_multiplier_0 multiplier = {(void*)0,(__Block_byref_multiplier_0 *)&multiplier, 0, sizeof(__Block_byref_multiplier_0), 6};
    
    int (*Block)(int) = ((int (*)(int))&__TestObject__testBlock1_block_impl_0((void *)__TestObject__testBlock1_block_func_0, &__TestObject__testBlock1_block_desc_0_DATA, (__Block_byref_multiplier_0 *)&multiplier, 570425344));
    
    ((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, 2);

}

Copy the code

Use does not equal assignment

Pen test

    // Question: Does array need to be decorated with __block
    NSMutableArray * array = [[NSMutableArray alloc] init];
    void (^Block)(void) = ^{
        [array addObject:@123];
    };
    Block();
    NSLog(@"array = %@",array);
Copy the code

Not needed here, just for use. Whether the object type is a primitive data type or an object type, assignment requires the addition of a __block modifier.

When I assign to a variable,A __block modifier is required

Block memory Management

Block Copy operation

The same __block variable can be accessed from any memory location.

A circular reference

_array = [NSMutableArray arrayWithObject:@"block"]; _strBlk = ^NSString *(NSString *num) { return [NSString stringWithFormat:@"helloc_%@",_array[0]]; }; _strBlk(@"hello"); _array = [NSMutableArray arrayWithObject:@"block"]; __weak NSArray * weakArray = _array; _strBlk = ^NSString *(NSString *num) { return [NSString stringWithFormat:@"helloc_%@",weakArray[0]]; }; _strBlk(@"hello");Copy the code

Under MRC, circular references are not generated. Under ARC, circular references are generated, causing memory leaks.

If not called or not called for a long time, the ring will always exist.