block 底层数据结构
测试代码:
1 2 3 4 5 6 7 8 9
| int main(int argc, const char * argv[]) { @autoreleasepool { void (^block)(void) = ^{ NSLog(@"block1234"); }; block(); } return 0; }
|
用 clang
编译查看 .cpp 文件, 看block被编译成了什么:
1 2 3 4 5 6 7 8
| void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
去掉上面的强制类型转换如下: void (*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA))
block->FuncPtr(block)
|
可以看到最终block的调用就是 block->FuncPtr(block);
, 就是调用函数
看一下block的 __main_block_impl_0
的结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 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; __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) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_y5_bv8t9pxx6jg830_kh4l6p_800000gn_T_main_fc5269_mi_0); }
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)};
|
可以看到block其实就是一个结构体,再仔细看一下还有isa指针,是不是跟我们前面讲到的对象一样,所以block也是OC的对象,里面有 struct __block_impl impl;
和 struct __main_block_desc_0* Desc;
结构, 而 __block_impl
里面有isa指针和 void *FuncPtr;
,不过结构体里面有构造函数 __main_block_impl_0
.
可以分析block在编译的时候是这样的: 根据block里面的实现先生成一个函数 __main_block_func_0
,然后接着生成一个 __main_block_impl_0
类型的结构体,将生成的函数指针和 __main_block_desc_0
传入并调用构造函数初始化block,这样block的结构就生成了.
当我们调用block时其实就是根据 __block_impl
查找函数指针 void *FuncPtr
,然后调用对应的函数的过程.
block 变量的捕获
我们来针对三种方式来测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| int weight = 10;
int main(int argc, const char * argv[]) { @autoreleasepool { auto int age = 10; static int height = 10; void (^block1)(void) = ^{ NSLog(@"block1--- %d %d %d", age, height, weight); }; age = 20; height = 20; weight = 20; block1(); } return 0; }
|
重新用clang编译查看结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| int weight = 10;
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; } }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int age = __cself->age; int *height = __cself->height;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_k8_chp8jt5d231d16l0s6w3c_gh0000gn_T_main_53994b_mi_0, age, (*height), weight); }
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[]) { { __AtAutoreleasePool __autoreleasepool;
auto int age = 10; static int height = 10;
void (*block1)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &height));
age = 20; height = 20; weight = 20;
((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1); } return 0; }
|
可以看到block里面只捕捉到了局部变量,而且捕捉到的局部变量的类型不一样, auto
类型的为值传递,而 static
类型的为指针传递.全局变量类型的为直接访问.
block能捕获到局部变量,不能捕获到全局变量.