iOS Block学习笔记(四) -- Block截获自动变量的

2018-11-23  本文已影响15人  brownfeng

前面, 我们知道Block本质是一个结构体, 当Block内部使用Block外部定义的自动变量, Block会截获这个自动变量的, 那么底层是如何实现的呢, 有如下源码:

int main() {
  int a = 100;
  int b = 200;
  const char *ch = "b = %d\n";
  void (^blk)(void) = ^{
    printf(ch,b);
  };

  b = 300;
  ch = "value had changed. b = %d\n";
  blk();
  return 0;
}
// 结果打印: b = 200;

转换成c++代码以后, 如下:

//1. Block "基类"
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
 
//2. 实际的Block的特定的Block"类", 继承自"__block_impl 基类", 并且注意增加了两个成员变量`const char *ch`, `int b`, 并且构造函数也需要传入这两个成员变量进行初始化.因此Block是通过成员变量来截获自动变量的瞬时值.
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;

  const char *ch;// 新增成员变量, 用来保存截获的自动变量的值
  int b;//新增成员变量, 用来保存截获的自动变量的值

  // 构造函数需要传入自动变量, 并且截获自动变量的瞬时值
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_ch, int _b, int flags=0) : ch(_ch), b(_b) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

//3. 成员函数, 在执行时, 是的是Block变量的成员变量
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  // 根据Block内部截获的自动变量的值, 调用具体的Block的方法
  const char *ch = __cself->ch; // bound by copy
  int b = __cself->b; // bound by copy

  printf(ch,b);
}

//4. Block描述
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

int main(){
  int a = 100;
  int b = 200;
  const char *ch = "b = %d\n";

  // 创建Block变量, 隐藏步骤: 传入需要被截获的自动变量当做参数, 这里是 ch 和 b. 由于变量a没有在Block中使用, 因此不会被截获.
  void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, ch, b));
  
  b = 300;
  ch = "value had changed.b = %d\n";
  // 执行Block函数时, Block实例时, 会使用实例的成员变量, 因此 b, ch的改变并不会影响被截获的值.
  ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); // 可以简写成 (*blk)->FuncPtr(blk);
  return 0;
}

总结: Block在创建时, 截获自动变量是用底层的成员变量保存自动变量的瞬时值, 因此在Block的函数内部, 是无法直接修改原自动变量的值的(因为修改的是Block的成员变量). 同时, 在Block语法执行以后, 再修改外部自动变量的值, 是不会影响Block内部截获的自动变量的值的.

参考资料

  1. <<Objective-C 高级编程: iOS与OSX多线程和内存管理>>
  2. https://blog.csdn.net/deft_mkjing/article/details/53149629
上一篇下一篇

猜你喜欢

热点阅读