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 copy
NSLog((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)。