1、block修改auto变量
int main(int argc, const char * argv[]) {@autoreleasepool {int age = 10;XLBlock block = ^{age = 20;NSLog(@"age = is %d",age);};block();}return 0;}
如果在block内部修改auto变量,会报错误:
! Variable is not assignable (missing block type specifier) (变量不可分配(缺少 block 类型说明符))
为什么报错可以通过查看.cpp文件,block真正执行的函数是__main_block_func_0:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {int age = __cself->age; // bound by copyNSLog((NSString *)&__NSConstantStringImpl__var_folders_......,age);}
但是age的作用域是main函数,所以如果在__main_block_func_0函数内修改age就会超出作用域。
所以在block内部不可以修改auto变量。
2、block修改static局部变量、全局变量
同样的代码,将age用static修饰:
int main(int argc, const char * argv[]) {@autoreleasepool {static int age = 10;XLBlock block = ^{age = 20;NSLog(@"age = is %d",age);};block();}return 0;}
系统并不会报错,因为block捕获的static变量的指针,所以在block内部可以修改static类型变量。
同理,全局变量因为不限制作用域,所以在block内部可以修改全局变量。
3、__block修饰符
block可以用于解决block内部无法修改auto变量值的问题(block不能修饰全局变量、静态变量(static)),代码举例:
int main(int argc, const char * argv[]) {@autoreleasepool {__block int age = 10;XLBlock block = ^{age = 20;NSLog(@"age = is %d",age);};block();}return 0;}
这样就可以修改auto变量了,且代码不会报错。
转成C++文件:
// __block变量结构体struct __Block_byref_age_0 {void *__isa;__Block_byref_age_0 *__forwarding;int __flags;int __size;int age;};// block结构体struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;__Block_byref_age_0 *age; // by ref};
可以观察到,编译器会将block变量包装成一个对象,保存在block的结构体中,结构示意图:
block封装的变量结构:
当在block内部修改变量时,就会查找blcok变量,找到内部的val值进行修改,可以通过查看block真正执行的函数main_block_func_0查看:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {// 找到封装的__blcok变量__Block_byref_age_0 *age = __cself->age; // bound by ref// 找到__blcok变量内部保存的age进行修改、打印(age->__forwarding->age) = 20;NSLog((NSString *)&__NSConstantStringImpl__var_folders_......,(age->__forwarding->age));}
至于为什么block变量内部有一个指向自己的指针?block是如何引用block变量的?block变量是如何引用被修饰的变量的?这些问题请参考下一篇文章[block内存管理](https://www.yuque.com/mengxianliang/uuxpmg/aya5l8)。
