__block变量内存布局是怎样的复杂结构?

摘要:1 内存布局 按照LLVM工程源码Block_private.h中的定义,__block变量的内存布局如下: struct Block_byref { void *isa; struct Block_byref *forwarding; i
1 内存布局 按照LLVM工程源码Block_private.h中的定义,__block变量的内存布局如下: struct Block_byref { void *isa; struct Block_byref *forwarding; int flags; int size; // 可选 void (*byref_keep)(struct Block_byref *dst, struct Block_byref *src); // 可选 void (*byref_destroy)(struct Block_byref *); // 可选 void *variable_layout; // 变量 Type variable; }; isa通常会被赋值为0,后面会有介绍。 forwarding指针在__block变量定义时会指向Block_byref自身。 当Block发生copy时它的值会有变动,会放到Block的copy中写。 flags是标志位,后面会有介绍。 size是当前结构体所占用的字节大小。 byref_keep与Block的copy相关,只有在满足条件时才有,后面会有介绍。 byref_destory与Block的释放相关,只有在满足条件时才有,后面会有介绍。 variable_layout在__block修饰结构体Struct时才会有,后面会介绍。 variable是被定义成__block的变量。 从上图可以看到,当一个Block捕获了一个__block变量时,它的Block_Descriptor中会有copy_helper和dispose_helper。 因为Block_byref也有isa指针,虽然它不能作为一个OC对象看待,但是从结构上看,也符合BLOCK_HAS_COPY_DISPOSE被设置的条件。 但是结构体Block_byref中的byref_keep和byref_destroy仍是可选的。 下面用一个例子来直观感受一下: void blockTest() { // __block 变量 __block int bi = 4; void(^blk)(int, int, int) = ^(int i, int j, int k) { int result = i + j + k + bi; NSLog(@"%d", result); }; // block 外操作 bi bi++; } 变量bi是一个__block变量。 使用clang的rewirte-objc将上面的代码重写为c++代码如下: void blockTest() { // __block 变量 __attribute__((__blocks__(byref))) __Block_byref_bi_0 bi = {(void*)0,(__Block_byref_bi_0 *)&bi, 0, sizeof(__Block_byref_bi_0), 4}; void(*blk)(int, int, int) = ((void (*)(int, int, int))&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, (__Block_byref_bi_0 *)&bi, 570425344)); // block 外操作 bi (bi.__forwarding->bi)++; } 可以看到__block变量被编译后,成为了Block_byref结构体: struct __Block_byref_bi_0 { void *__isa; __Block_byref_bi_0 *__forwarding; int __flags; int __size; int bi; }; 即使在Block外部访问bi变量,也是通过这个结构体的forwarding指针进行访问。
阅读全文