This paper is the nature of notes, mainly for their own understanding of the place is not very thorough record.

Recommend system directly learn small code brother iOS underlying principle class –MJ teacher’s class is really good, push a wave.


A basic understanding

A block is actually a block of code

Called only when () is executed at the end

^{
    NSLog(@"this is a block!");
    NSLog(@"this is a block!");
    NSLog(@"this is a block!");
    NSLog(@"this is a block!");
};
Copy the code

Take a block

// Block with no arguments. ^{void (^block)() = ^(){}; block(); // block void (^block)(int, int) = ^(int a, int b){NSLog(@"a=%d,b=%d",a,b);
};
block(20, 10);
Copy the code

Void (^block)(int, int) returns (^ name)(argument 1, argument 2)


The nature of the block

An OC object that encapsulates the function call and the calling environment

1. Block is also an OC object in nature, with an ISA pointer inside

2. Block encapsulates the function call and the environment (parameters) required for the function call.

You can learn from the CPP file:

int age = 20;

void (^block)(int, int) =  ^(int a , int b){
    NSLog(@"this is a block! -- %d", age);

};


block(10, 10);
Copy the code

The block will eventually be converted to a structure of the following format (details vary for each block)

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; // Block information (such as size) int age; // the c++ constructor age(_age) means that _age will be automatically assigned to 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 a, int b) { int age = __cself->age; // Take the variables that block originally captured, Assign to the executing function // function call NSLog((NSString) *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_0, age); } struct __block_impl { void *isa; // indicate that the block also belongs to the OC object int Flags; int Reserved; void *FuncPtr; };Copy the code

The function call is wrapped separately as a __main_block_func_0 method. When a block is defined, the block structure is passed in.

void (*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
Copy the code

FuncPtr = fp; FuncPtr = fp; To store the function address inside the block.

Finally, when a block is called, take FuncPtr, passing in the argument to perform the call.

((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10);
Copy the code

Simplify the block statement of CPP

Construct a block structure

void (*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); /** Remove the strong syntax after the function address, block description (size, etc.) information, need to be captured variables. The __main_block_IMPL_0 block structure is returned, And hold/void * * (* block) (int, int) = & __main_block_impl_0 (__main_block_func_0, & __main_block_desc_0_DATA, age));Copy the code

Call the block structure

((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10); /** get the address of the function from the structure, pass in the argument and call. Blcok is passed because each block object has a different variable trapped inside **/ block->FuncPtr(block, 10, 10);Copy the code

Variable to capture

To ensure that external variables can be accessed within a block, blocks have a variable capture mechanism

Auto variable

Local variables in C are auto variables by default. So auto refers to local variables

The life of the auto variable is destroyed as it goes out of scope. To ensure that the block executes properly, the auto variable passes its value to the block’s constructor when caught by the block.


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int a = 10;
        void (^block)(void) = ^{
            NSLog(@"age is %d", a); //age is 10
        };
        a = 20;
        block();
    }
    return0; } struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; // Block information (size, etc.) int age; // the c++ constructor age(_age) means that _age will be automatically assigned to 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 a, int b) { int age = __cself->age; // Take the variables that block originally captured, Assign to the executing function // function call NSLog((NSString) *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_0, age); } // Build blocks in main. To a value passed to the constructor of the void * block () = ((void (*) ()) & __test_block_impl_0 ((void *) __test_block_func_0, & __test_block_desc_0_DATA, a));Copy the code

Static variables

Because static variables live in memory, their use is limited to the scope. So when a static variable is captured by a block, the block executes as long as you pass a pointer (address) to the value of the variable to the constructor.


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        static int b = 10;
        void (^block)(void) = ^{
            NSLog(@"height is %d", b); //height is 20
        };
        b = 20;
        block();
    }
    return0; } struct __test_block_impl_0 { struct __block_impl impl; struct __test_block_desc_0* Desc; int *b; Void *fp, struct __test_block_desc_0 *desc, int *_b, int flags=0) : b(_b) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __test_block_func_0(struct __test_block_impl_0 *__cself) { int *b = __cself->b; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_fd2a14_mi_0, (*b)); } / / in the main building block will b a pointer is passed to the constructor of the void * block () = ((void (*) ()) & __test_block_impl_0 (__test_block_func_0 (void *), &__test_block_desc_0_DATA, &b));Copy the code

The global variable

Since the life of a global variable resides in memory, its use is not scoped. Therefore, global variables do not need to be captured. When a block is called, use global variables directly to ensure that the block executes properly.

int age_ = 10;
static int height_ = 10;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^block)(void) = ^{
            NSLog(@"age_ is %d ,height_ is %d", age_,height_);
        };
        age_ = 20;
        height_ = 20;
        block();
    }
    return0; } int age_ = 10; static int height_ = 10; Struct __main_block_impl_0 {struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; Static void __main_block_func_0(struct __main_block_impl_0 *__cself) { Direct use of global variables NSLog ((nsstrings *) & __NSConstantStringImpl__var_folders_tz_hcmmb5t57v1cr81ydm6s5s140000gn_T_main_6f323a_mi_0, age_,height_); } 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, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); // The block constructor does not pass the global variable age_ = 20; height_ = 20; ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); }return 0;
}

Copy the code

The type of the block

Block with the class

Using the class method, you can also prove that the block is an OC object in nature

void test()
{
    void (^block)(void) = ^{
        NSLog(@"Hello");
    };
    
    NSLog(@"% @", [block class]); //__NSGlobalBlock__ (other types of blocks are __NSStackBlock__ or __NSMallocBlock__) NSLog(@"% @", [[block class] superclass]); //__NSGlobalBlock NSLog(@"% @", [[[block class] superclass] superclass]); //NSBlock NSLog(@"% @", [[[[block class] superclass] superclass] superclass]); //NSObject }Copy the code

Block of isa

In the block structure, ISA is specified as the value of _NSConcreteXXXBlock. This value is the type of block.

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; NSObject *obj; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_obj, int flags=0) : obj(_obj) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

Three block types

There are three types of blocks, which you can see by calling the class method or isa pointer. They are all derived from the NSBlock type

  • NSGlobalBlock (_NSConcreteGlobalBlock)

  • NSStackBlock (_NSConcreteStackBlock)

  • NSMallocBlock (_NSConcreteMallocBlock)

From top to bottom: Low address — High address non-ARC source of blocks

Block and copy

Under the ARC

By default, most blocks are on the stack as __NSStackBlock__ (blocks that don’t access the auto variable are in the global extents).

When __NSStackBlock__ is out of scope, the block structure can be contaminated with modifications.

Copy __NSStackBlock__ and move it to the heap for management.

__NSStackBlock__ will then be changed to __NSMallocBlock__

This is why blocks are always declared as a copy property (although ARC strong is automatically copied).

Calling copy does different things for different types of blocks

The block under the ARC

In an ARC environment, the compiler automatically copies blocks on the stack to the heap as needed, such as the following

  1. Block as a function return value
MJBlock myblock()
{
    return ^{
        NSLog(@"-- -- -- -- -- -- -- -- --");
    };
}
Copy the code
  1. Assign block to the __strong pointer
MJBlock block = ^{
    NSLog(@"---------%d", age);
};
Copy the code
  1. Block is used as a Cocoa API method name containing a method parameter called usingBlock
[@[] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    
}];
Copy the code
  1. Block as a method parameter of the GCD API
Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{});Copy the code

Block and object type auto variable

(ARC environment)

When a block captures the auto variable of the object type. The __main_block_desc_0 structure has __main_block_copy_0 and __main_block_dispose_0 functions inside, and the heap object returns and releases appropriately when the block moves into the heap space.

I’ll post a CPP code (auto variable)

int main(int argc, const char * argv[]) { @autoreleasepool { NSObject *obj = [NSObject new]; //OC object void (^block)(void) = ^{NSLog(@"obj is %@", obj); / / capture}; obj = nil; block(); }return0; } struct __main_block_impl_0 {struct __block_impl impl; struct __main_block_desc_0* Desc; // If the external is __weak the internal is __weak NSObject * __strong obj; // For the auto variable, the structure holds the same variable as before. __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_obj, int flags=0) : obj(_obj) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { NSObject *obj = __cself->obj; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_tz_hcmmb5t57v1cr81ydm6s5s140000gn_T_main_d73acf_mi_0, obj); } // This method is executed when a block is moved from the stack to the heap. static void __main_block_copy_0(struct __main_block_impl_0*dst, Struct __main_block_impl_0* SRC (struct __main_block_impl_0* SRC, struct __main_block_impl_0* SRC, struct __main_block_impl_0* SRC, struct __main_block_impl_0* SRC, struct __main_block_impl_0* SRC) Retain or retain _Block_object_assign((void*)& DST ->obj, (void*) SRC ->obj, 3/*BLOCK_FIELD_IS_OBJECT*/); } // This method is executed when a block is removed (freed) from the heap. Static void __main_block_dispose_0(struct __main_block_impl_0* SRC) _Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/); } // Block description. There are two more variables than basic types. Static struct __main_block_desc_0 {size_t reserved; size_t Block_size; void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); void (*dispose)(struct __main_block_impl_0*); } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0}; Int main(int argc, const char * argv[]) {/* @autoreleasepool */ {__AtAutoreleasePool __autoreleasepool; NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new")); // When a block is constructed, OC object into the void block (*) (void) = ((void (*) ()) & __main_block_impl_0 ((void *) __main_block_func_0, & __main_block_desc_0_DATA, obj, 570425344)); ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); }return 0;
}

Copy the code

The capture aspect of an object

Basically also conforms to the above law, take local variables as an example.

Except in the case of objects, we’re passing Pointers. In addition, to ensure that OC objects are properly accessed within the block, they are strongly referenced to extend the object life cycle.

For blocks on the stack

If the block is on the stack, there is no strong reference to the auto variable

When a block is copied to the heap

The internal _Block_object_assign function is executed, and the type of the auto object captured inside the structure (__strong/__weak) determines how it is referenced.

When a block is removed from the heap

Internal _Block_object_dispose function is executed to release the auto object captured in the structure body.


__block

The compiler wraps the __block variable into an object (__Block_byref_age_0), and the declared value exists as an attribute of the object.


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        __block int age = 10;
        
        MJBlock block = ^{
            __strong int myage = age;
            age = 20;
            NSLog(@"age is %d", age);
        };

        
        block();
        
    }
    return0; } struct __Block_byref_age_0 {void *__isa; __Block_byref_age_0 *__forwarding; int __flags; int __size; int age; }; Struct __main_block_impl_0 {struct __block_impl impl; struct __main_block_desc_0* Desc; NSObject *p; // no longer int age; __Block_byref_age_0 *age; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_p, __Block_byref_age_0 *_age, int flags=0) : p(_p), age(_age->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; Static void __main_block_func_0(struct __main_block_impl_0 *__cself) {__Block_byref_age_0 *age = __cself->age; // bound byref // Get the age variable from the __Block_byref_age_0 structure and modify (age->__forwarding->age) = 20; NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_e2457b_mi_0); } //c++ main function int main(int argc, const char * argv[]) {/* @autoreleasepool */ {__AtAutoreleasePool __autoreleasepool;  //__block int age = 10; __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10}; // equivalent to __Block_byref_age_0 age = {0, &age, 0, sizeof(__Block_byref_age_0), 10}; Block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_data, p, (__Block_byref_age_0 *)&age, 570425344)); // equivalent to MJBlock block = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_data, p, &age, 570425344); ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); }return 0;
}
Copy the code

External use of a __block object

You actually operate on the age pointer in the structure, not the age pointer in the structure.


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        __block int age = 10;
        age = 50;
    }
    return 0;
}



int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
        (age.__forwarding->age) = 50;
    }
    return 0;
}
Copy the code

__forwarding pointer

__forwarding refers to the object entity wrapped by __block to ensure correctness when used.

  • When a block is on the stack,__forwardingThe entity referred to is on the stack__blockWrap objects.

  • When the block moves onto the heap,__blockThe wrapped object is also copied in the heap. And the two__forwardingThe Pointers all point to the heap__blockWrap objects.

__block decorates OC objects

Strong and weak references to OC objects are not represented in the block structure (both strong), but in the __Block_byref structure.

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        MJPerson *person = [[MJPerson alloc] init];
        
        __block __weak JSPerson *weakPerson = person;
        MJBlock block = ^{
            NSLog(@"%p", weakPerson);
        };

        block();

    }
    return0; } struct __Block_byref_weakPerson_0 { void *__isa; // 8 __Block_byref_weakPerson_0 *__forwarding; // 8 int __flags; // 4 int __size; // 4 void (*__Block_byref_id_object_copy)(void*, void*); // When a block is moved to the heap, the __Block_byref object is executedreturnVoid (*__Block_byref_id_object_dispose)(void*); // When a block is removed from the heap, the __Block_byref object is released MJPerson *__weak weakPerson; // The __block wrapped structure actually contains weak references. }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_weakPerson_0 *weakPerson; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

Three known memory address segments

NSLog(@"Age %p", &age);
NSLog(@"Stack: a %p", &a);
NSLog(@"Heap: obj %p", [[NSObject alloc] init]);
Copy the code