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
- Block as a function return value
MJBlock myblock()
{
return ^{
NSLog(@"-- -- -- -- -- -- -- -- --");
};
}
Copy the code
- Assign block to the __strong pointer
MJBlock block = ^{
NSLog(@"---------%d", age);
};
Copy the code
- 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
- 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,
__forwarding
The entity referred to is on the stack__block
Wrap objects.
- When the block moves onto the heap,
__block
The wrapped object is also copied in the heap. And the two__forwarding
The Pointers all point to the heap__block
Wrap 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