学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

Block变量捕获

变量类型 是否捕获到 block 内部 访问方式
全局变量 直接访问
局部变量(auto 类型) 值传递
局部变量(static 类型) 指针传递

auto 类型的局部变量

auto 类型的局部变量(我们定义出来的变量,默认都是 auto 类型,只是省略了),block 内部会自动生成一个同类型成员变量,用来存储这个变量的值,访问方式为值传递auto 类型的局部变量可能会销毁,其内存会消失,block 将来执行代码的时候不可能再去访问那块内存,所以捕获其值。由于是值传递,我们修改 block 外部被捕获变量的值,不会影响到 block 内部捕获的变量值。

对象类型的局部变量

对于对象类型的局部变量,block 会连同它的所有权修饰符一起捕获。

  • 如果 block 是在栈上,将不会对对象产生强引用
  • 如果 block 被拷贝到堆上,将会调用 block 内部的 copy(__funcName_block_copy_num)函数,copy 函数内部又会调用 assign(_Block_object_assign)函数,assign 函数将会根据变量的所有权修饰符做出相应的操作,形成强引用(retain)或者弱引用。
  • 如果 block 从堆上移除,也就是被释放的时候,会调用 block 内部的 dispose(_Block_object_dispose)函数,dispose 函数会自动释放引用的变量(release)。

对于 __block 修饰的变量

对于 __block(可用于解决 block 内部无法修改 auto 变量值的问题) 修饰的变量,编译器会将 __block 变量包装成一个 __Block_byref_varName_num 对象。它的内存管理几乎等同于访问对象类型的 auto 变量,但还是有差异。

  • 如果 block 是在栈上,将不会对 __block 变量产生强引用
  • 如果 block 被拷贝到堆上,将会调用 block 内部的 copy 函数,copy 函数内部又会调用 assign 函数,assign 函数将会直接对 __block 变量形成强引用(retain)。
  • 如果 block 从堆上移除,也就是被释放的时候,会调用 block 内部的 dispose 函数,dispose 函数会自动释放引用的 __block 变量(release)。

__block 的 __forwarding 指针

1
2
3
4
5
6
7
8
9
__block int age = 10;
void(^block)(void) = ^{
age = 20;
NSLog(@"block-%d",age);
};
block();
NSLog(@"%d",age);
// block-20
// 20

为什么要通过 age 结构体里的__forwarding指针拿到 age 变量的值,而不直接 age 结构体拿到 age 变量的值呢?

__block__forwarding是指向自己本身的指针,
为了不论在任何内存位置,都可以顺利的访问同一个 __block 变量。

  • block 对象 copy 到堆上时,内部的 __block 变量也会 copy 到堆上去。为了防止 age 的值赋值给栈上的 __block 变量,就使用了__forwarding
  • __block 变量在栈上的时候,__block 变量的结构体中的__forwarding指针指向自己,这样通过__forwarding取到结构体中的 age 给它赋值没有问题;
  • __block 变量 copy 到堆上后,栈上的__forwarding指针会指向 copy 到堆上的 _block 变量结构体,而堆上的__forwarding指向自己;

这样不管我们访问的是栈上还是堆上的 __block 变量结构体,只要是通过__forwarding指针访问,都是访问到堆上的 __block 变量结构体;给 age 赋值,就肯定会赋值给堆上的那个 __block 变量中的 age。

参考

block 的变量捕获机制
Block 详解