iOS __block 到底做了啥

如果想在block内部修改从外部捕获的auto变量的值,可以在该auto变量定义的时候,加上关键字__block

typedef void(^CLBlock)(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        __block int a = 10;
        int b = 30;
        CLBlock myblock = ^{
            a = 20;
            NSLog(@"%d",b);
        };
        
        myblock();
        
        NSLog(@"myblock执行完之后,a = %d",a);
    }
    
    return 0;
}
输出结果:
Block学习[29867:3904669] 30
Block学习[29867:3904669] myblock执行完之后,a = 20

__block只可以用来作用于auto变量,它的目的就是为了能够让auto变量能够在block内部内修改。而全局变量和static变量本来就可以从block内部进行修改,因此__block对它们来说没有意义,所以__block被规定只能用于修饰auto变量

我们把main.m 文件 改成main.cpp文件看下block到底是什么。

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int b;
  __Block_byref_a_0 *a; // by ref
  __main_block_impl_0(void *fp,
                      struct __main_block_desc_0 *desc,
                      int _b,
                      __Block_byref_a_0 *_a,
                      int flags=0) : b(_b), a(_a->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

为了比较,我特意加了一个int b作为对比。
基本类型的auto变量被block捕获的时候,就是通过值拷贝的形式把值赋给block内部相对应的基本类型变量。而例子里的__block int a = 10,我们可以看到在底层,系统是把int a包装到了一个叫__Block_byref_a_0的对象里面。
看下__block_byref_a_0的对象里有啥:

struct __Block_byref_a_0 {
  void *__isa;//有isa,是一个对象
__Block_byref_a_0 *__forwarding;//指向自身类型对象的指针
 int __flags;//不用关心
 int __size;//自己所占大小
 int a;//被封装的 基本数据类型变量
};

这样能看出来,外面使用__block 修饰的int a 在__block_byref_a_0对象里,就是最后的int a 这个结构体里的int a 也就是我们在block里需要调用的a。

总结

1、在block外部 使用__block修饰变量。
2、在block内部使用被修饰的变量时,会创建一个__Block_byref_xx_0 *_xx对象。(此处的xx为变量名,下同)
3、被创建的对象,会生成__Block_byref_xx_0 *__forwarding;//指向自身类型对象的指针
4、最后__forwarding这个指针指向最新的值,然后再指回__Block_byref_xx_0 *_xx
5、最终当我们打印的时候,如果变量被__block修饰了,会被内部修改。

你可能感兴趣的:(iOS __block 到底做了啥)