找回密码
 立即注册
首页 业界区 业界 __block 变量内存布局详解

__block 变量内存布局详解

裴涛 前天 01:10
1 内存布局

按照LLVM工程源码Block_private.h中的定义,__block变量的内存布局如下:
  1. struct Block_byref {
  2.     void *isa;
  3.     struct Block_byref *forwarding;
  4.     int flags;
  5.     int size;
  6.     // 可选
  7.     void (*byref_keep)(struct Block_byref *dst, struct Block_byref *src);
  8.     // 可选
  9.     void (*byref_destroy)(struct Block_byref *);
  10.     // 可选
  11.     void *variable_layout;
  12.     // 变量
  13.     Type variable;
  14. };
复制代码
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的变量。
1.png

从上图可以看到,当一个Block捕获了一个__block变量时,它的Block_Descriptor中会有copy_helper和dispose_helper。
因为Block_byref也有isa指针,虽然它不能作为一个OC对象看待,但是从结构上看,也符合BLOCK_HAS_COPY_DISPOSE被设置的条件。
但是结构体Block_byref中的byref_keep和byref_destroy仍是可选的。
下面用一个例子来直观感受一下:
  1. void blockTest() {
  2.   // __block 变量
  3.   __block int bi = 4;
  4.   void(^blk)(int, int, int) = ^(int i, int j, int k) {
  5.     int result = i + j + k + bi;
  6.     NSLog(@"%d", result);
  7.   };
  8.   
  9.   // block 外操作 bi
  10.   bi++;
  11. }
复制代码
变量bi是一个__block变量。
使用clang的rewirte-objc将上面的代码重写为c++代码如下:
  1. void blockTest() {
  2.   // __block 变量
  3.   __attribute__((__blocks__(byref))) __Block_byref_bi_0 bi = {(void*)0,(__Block_byref_bi_0 *)&bi, 0, sizeof(__Block_byref_bi_0), 4};
  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));
  5.   // block 外操作 bi
  6.   (bi.__forwarding->bi)++;
  7. }
复制代码
可以看到__block变量被编译后,成为了Block_byref结构体:
  1. struct __Block_byref_bi_0 {
  2.   void *__isa;
  3. __Block_byref_bi_0 *__forwarding;
  4. int __flags;
  5. int __size;
  6. int bi;
  7. };
复制代码
即使在Block外部访问bi变量,也是通过这个结构体的forwarding指针进行访问。
在Block内部访问bi变量,也是通过forwarding指针:
  1. struct __blockTest_block_impl_0 {
  2.   struct __block_impl impl;
  3.   struct __blockTest_block_desc_0* Desc;
  4.   // Block 捕获的 __block 变量 bi
  5.   __Block_byref_bi_0 *bi; // by ref
  6. };
  7. static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself, int i, int j, int k) {
  8.   __Block_byref_bi_0 *bi = __cself->bi; // bound by ref
  9.    
  10.     // Block 内部访问 bi 变量
  11.     int result = i + j + k + (bi->__forwarding->bi);
  12.     NSLog((NSString *)&__NSConstantStringImpl__var_folders_p6_jy49zvqx2656qb8rbq64hc_w0000gn_T_Block_de3226_mi_0, result);
  13. }
复制代码
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

相关推荐

您需要登录后才可以回帖 登录 | 立即注册