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
的值。