美文网首页
iOS Block学习笔记(四) -- Block截获自动变量的

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

作者: brownfeng | 来源:发表于2018-11-23 16:03 被阅读15次

    前面, 我们知道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

    相关文章

      网友评论

          本文标题:iOS Block学习笔记(四) -- Block截获自动变量的

          本文链接:https://www.haomeiwen.com/subject/vnijqqtx.html