__block 变量内存布局详解
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:
// Flags stored in __block variables.enum BlockByrefFlags {BLOCK_BYREF_HAS_COPY_DISPOSE = (1
页:
[1]