变量类型 | 是否捕获到 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 | __block int age = 10; |
为什么要通过 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。