int main(int argc, const char * argv[]) { @autoreleasepool { int age = 10; void (^block)(int, int) = ^(int a , int b){ NSLog(@"this is a block! -- %d", age); NSLog(@"this is a block!"); NSLog(@"this is a block!"); NSLog(@"this is a block!"); }; block(10, 10); } return 0; }
使用命令行将代码转化为c++与OC代码进行比较
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
命令代码ios
// 编译后代码 int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; int age = 10; void (*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); ((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10); } return 0; }
定义block变量
代码c++
// 定义block变量代码 void(*block)(int ,int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); // 能够简化为下列 // void (*block)(int, int) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, age);
上述定义代码中,能够发现,block定义中调用了__main_block_impl_0函数,而且将__main_block_impl_0函数的地址赋值给了block。那么咱们来看一下__main_block_impl_0函数内部结构。sass
__main_block_imp_0结构体iphone
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; } };
(void *)__main_block_func_0
参数函数
```C++ static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) { int age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_0, age); NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_1); NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_2); NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_3); } ```
&__main_block_desc_0_DATA
参数this
C++ 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)};
命令行
age
参数
此时回过头来查看__main_block_impl_0结构体3d
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;// block 内部代码块地址 Desc = desc;// 存储block 对象占用的内存大小 } };
首先咱们看一下__block_impl第一个变量就是__block_impl结构体。指针
// __block_impl结构体内部 struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
咱们能够发现__block_impl结构体内部就有一个isa指针。所以能够证实block本质上就是一个oc对象。而在构造函数中将函数中传入的值分别存储在__main_block_impl_0结构体实例中,最终将结构体的地址赋值给block。code
调用block执行内部代码
((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10);
上面咱们知道,FunPtr中存储着经过代码块封装的函数地址,那么调用此函数,也就是会执行代码块中的代码。而且回头查看__main_block_func_0函数,能够发现第一个参数就是__main_block_impl_0类型的指针。也就是说将block传入__main_block_func_0函数中,便于重中取出block捕获的值。
经过代码证实一下上述内容:
#import <Foundation/Foundation.h> struct __main_block_desc_0 { size_t reserved; size_t Block_size; }; struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; }; int main(int argc, const char * argv[]) { @autoreleasepool { int age = 10; void (^block)(int, int) = ^(int a , int b){ NSLog(@"this is a block! -- %d", age); NSLog(@"this is a block!"); NSLog(@"this is a block!"); NSLog(@"this is a block!"); }; // 将底层的结构体强制转化为咱们本身写的结构体,经过咱们自定义的结构体探寻block底层结构体 struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block; block(10, 10); } return 0; }
经过打断点能够看出咱们自定义的结构体能够被赋值成功,以及里面的值。
接下来断点来到block代码块中,看一下堆栈信息中的函数调用地址。Debuf workflow -> always show Disassembly
经过上图能够看到地址确实和FuncPtr中的代码块地址同样。