Block捕获原理

124 阅读1分钟

Block的本质

我们将block代码编译成cpp文件

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void(^block)(void) = ^{         
        };
        block();
    }
    return 0;
}

// clang main.m -rewrite-objc -o main.cpp  生成的cpp文件代码很多,直接拉到底部
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
}
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 argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

我们整理一下,可以得出block的底层数据结构

struct __block_impl 
{ 
    void *isa; 
    int Flags; 
    int Reserved; 
    void *FuncPtr; 
};

通过上面,我们可以看出block的本质也是一个OC对象,有一个isa指针,FuncPtr表示执行代码块的函数地址

block捕获

  • 全局变量
  • 全局静态变量
  • 局部变量
  • 静态局部变量 截屏2022-01-27 11.33.00.png 将上面代码转成cpp Xnip2022-01-27_12-53-45.jpg 通过截图我们也就明白,局部变量,直接拷贝一份(看截图参数 int _y)也就是我们说的捕获,静态局部变量是指针传递(看截图参数 * _z),我们得出如下结论 截屏2022-01-27 11.04.42.png

__block

我们知道如果想要在block内部修改局部变量的值,这个局部变量需要用__block修饰, 接下来我们探索一下__block到底做了哪些事情 截屏2022-01-27 13.05.19.png 同样将上面代码转成cpp代码 Xnip2022-01-27_13-07-37.jpg

  • __isa指针
  • __forwarding我理解为传入变量的地址
  • y 变量的值 这样我们就可以知道 __block修饰的变量 在block内为什么可以被修改