block内修改变量的值
本部分分析基于下面代码。
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
Block block = ^ {
// age = 20; // 无法修改
NSLog(@"%d",age);
};
block();
}
return 0;
}
默认情况下block不能修改外部的局部变量。通过之前对源码的分析可以知道。
age是在main函数内部声明的,说明age的内存存在于main函数的栈空间内部,但是block内部的代码在__main_block_func_0函数内部。__main_block_func_0函数内部无法访问age变量的内存空间,两个函数的栈空间不一样,__main_block_func_0内部拿到的age是block结构体内部的age,因此无法在__main_block_func_0函数内部去修改main函数内部的变量。
方式一:age使用static修饰。
前文提到过static修饰的age变量传递到block内部的是指针,在__main_block_func_0函数内部就可以拿到age变量的内存地址,因此就可以在block内部修改age的值。
方式二:__block
__block用于解决block内部不能修改auto变量值的问题,__block不能修饰静态变量(static) 和全局变量
__block int age = 10;
编译器会将__block修饰的变量包装成一个对象,查看其底层c++源码。

上述源码中可以发现
首先被__block修饰的age变量声明变为名为age的__Block_byref_age_0结构体,也就是说加上__block修饰的话捕获到的block内的变量为__Block_byref_age_0类型的结构体。
通过下图查看__Block_byref_age_0结构体内存储哪些元素。

__isa指针 :__Block_byref_age_0中也有isa指针也就是说__Block_byref_age_0本质也一个对象。
__forwarding :__forwarding是__Block_byref_age_0结构体类型的,并且__forwarding存储的值为(__Block_byref_age_0 *)&age,即结构体自己的内存地址。
__flags :0
__size :sizeof(__Block_byref_age_0)即__Block_byref_age_0所占用的内存空间。
age :真正存储变量的地方,这里存储局部变量10。
接着将__Block_byref_age_0结构体age存入__main_block_impl_0结构体中,并赋值给__Block_byref_age_0 *age;

之后调用block,首先取出__main_block_impl_0中的age,通过age结构体拿到__forwarding指针,上面提到过__forwarding中保存的就是__Block_byref_age_0结构体本身,这里也就是age(__Block_byref_age_0),在通过__forwarding拿到结构体中的age(10)变量并修改其值。
后续NSLog中使用age时也通过同样的方式获取age的值。
