Block nature
- A block is also essentially an OC object with an ISA pointer inside it
- A block is an OC object that encapsulates a function call and its environment
Define a block
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
void(^block)(void) = ^(void){
NSLog(@"hello-world");
};
block();
}
return 0;
}
Copy the code
To see the underlying implementation of the block, run the xcrun-sdk iphoneOS clang-arch arm64-rewrite-objc main.m command line to get the main. CPP file
The main function code in main. CPP is as follows
int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; / / define the block variable void block (*) (void) = ((void (*) ()) & __main_block_impl_0 (__main_block_func_0 (void *), &__main_block_desc_0_DATA)); / / implementation code within the block (void (__block_impl *) (*)) ((__block_impl *) block) - > FuncPtr) ((__block_impl *) block); } return 0; }Copy the code
__main_block_impl_0 is the internal code of the __main_block_func_0 block that encapsulates the memory occupied by the block defined by the function __main_block_desc_0_DATA
The __main_block_IMPL_0 structure is as follows
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, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; // Block type impl.Flags = Flags; impl.FuncPtr = fp; // execute function address Desc = Desc; // Store __main_block_desc_0 (0, sizeof(__main_block_impl_0)))}}; struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; // the pointer points to the address of the function executing inside the block};Copy the code
__main_block_func_0
Static void __main_block_func_0(struct __main_block_impl_0 *__cself) {NSLog((NSString) *)&__NSConstantStringImpl__var_folders_2v_yzvmxk793t31txq41bn50r1r0000gn_T_main_f08b1f_mi_0); }Copy the code
__main_block_desc_0_DATA
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)};Copy the code
Eventually the block is converted to a __main_block_IMPL_0 structure object, which is assigned to the variable block, The passing arguments are __main_block_func_0 and __main_block_desc_0_DATA to execute the __main_block_IMPL_0 constructor, The __main_block_desc_0_DATA function is assigned to __main_block_IMPL_0 ->FuncPtr, and the executive function is block->FuncPtr(block), Before deleting redundant code is ((void (__block_impl *) (*)) ((__block_impl *) block) – > FuncPtr) ((__block_impl *) block); So why can block be cast directly to __block_impl? Since the first line of the __main_block_IMPL_0 structure is __block_impl, the memory address of __main_block_IMPL_0 is the same as the memory address of __block_IMPl, forcing the conversion will not be a problem.
Block variable capture
- To ensure that external variables can be accessed within a block, blocks have a variable capture mechanism
auto
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
int age = 10;
void (^block)(void) = ^(void){
NSLog(@"age is %d",age);
};
age = 20;
block();
}
return 0;
}
Copy the code
The main CPP code
int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; int age = 10; void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); age = 20; ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); } return 0; } struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; 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; }};Copy the code
You can see that age is passed directly into the block’s constructor as a value pass.
static
int age = 10;
static int height = 20;
void (^block)(void) = ^(void){
NSLog(@"age is %d height is %d",age,height);
};
age = 20;
height = 30;
block();
Copy the code
main.cpp
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 10;
static int height = 20;
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &height));
age = 20;
height = 30;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
Copy the code
The __main_block_IMPL_0 structure also starts with *, so the height value changes.
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; int *height; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
The global variable
Global variables are accessed directly without being captured in the __main_block_IMPL_0 structure.
__block qualifier
- __bllock can be used to solve the problem that the auto variable cannot be modified inside a block
- __block cannot modify global or static variables.
- The compiler wraps a __block variable into an object
code
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
__block int age = 10;
void(^block)(void) = ^(void){
age = 20;
NSLog(@"age is %d",age);
};
block();
}
return 0;
}
Copy the code
main.cpp
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; __Block_byref_age_0 *age; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; 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}; void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344)); ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); } return 0; }Copy the code
There is an age pointer to an __Block_byref_age_0 structure inside the block. We find that an age of type int is inside the structure.
That is, the __block variable is wrapped as an object by the compiler, and our member variable is placed inside the object.
If we look inside this __Block_byref_age_0, one of these variables that might be confusing is this __forwarding. It is a pointer to the structure itself. __forwarding->age (age->__forwarding->age)
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*);
Copy the code
There are also two more Pointers in the __main_block_desc_0 structure, which are memory management-related functions.