__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指针进行访问。
