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指针进行访问。
在Block内部访问bi变量,也是通过forwarding指针:- struct __blockTest_block_impl_0 {
- struct __block_impl impl;
- struct __blockTest_block_desc_0* Desc;
- // Block 捕获的 __block 变量 bi
- __Block_byref_bi_0 *bi; // by ref
- };
- static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself, int i, int j, int k) {
- __Block_byref_bi_0 *bi = __cself->bi; // bound by ref
-
- // Block 内部访问 bi 变量
- int result = i + j + k + (bi->__forwarding->bi);
- NSLog((NSString *)&__NSConstantStringImpl__var_folders_p6_jy49zvqx2656qb8rbq64hc_w0000gn_T_Block_de3226_mi_0, result);
- }
复制代码 forwarding指针的作用会放到Block的copy中写。
2 isa
__block变量结构体的isa指针会固定设置为0。
3 flags
在LLVM工程源码CGBlocks.h中定义了flags:
[code]// Flags stored in __block variables.enum BlockByrefFlags { BLOCK_BYREF_HAS_COPY_DISPOSE = (1 |