1、block修改auto变量

  1. int main(int argc, const char * argv[]) {
  2. @autoreleasepool {
  3. int age = 10;
  4. XLBlock block = ^{
  5. age = 20;
  6. NSLog(@"age = is %d",age);
  7. };
  8. block();
  9. }
  10. return 0;
  11. }

如果在block内部修改auto变量,会报错误:
! Variable is not assignable (missing block type specifier) (变量不可分配(缺少 block 类型说明符))
为什么报错可以通过查看.cpp文件,block真正执行的函数是__main_block_func_0:

  1. static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  2. int age = __cself->age; // bound by copy
  3. NSLog((NSString *)&__NSConstantStringImpl__var_folders_......,age);
  4. }

但是age的作用域是main函数,所以如果在__main_block_func_0函数内修改age就会超出作用域。
所以在block内部不可以修改auto变量。

2、block修改static局部变量、全局变量

同样的代码,将age用static修饰:

  1. int main(int argc, const char * argv[]) {
  2. @autoreleasepool {
  3. static int age = 10;
  4. XLBlock block = ^{
  5. age = 20;
  6. NSLog(@"age = is %d",age);
  7. };
  8. block();
  9. }
  10. return 0;
  11. }

系统并不会报错,因为block捕获的static变量的指针,所以在block内部可以修改static类型变量。
同理,全局变量因为不限制作用域,所以在block内部可以修改全局变量。

3、__block修饰符

block可以用于解决block内部无法修改auto变量值的问题(block不能修饰全局变量、静态变量(static)),代码举例:

  1. int main(int argc, const char * argv[]) {
  2. @autoreleasepool {
  3. __block int age = 10;
  4. XLBlock block = ^{
  5. age = 20;
  6. NSLog(@"age = is %d",age);
  7. };
  8. block();
  9. }
  10. return 0;
  11. }

这样就可以修改auto变量了,且代码不会报错。
转成C++文件:

  1. // __block变量结构体
  2. struct __Block_byref_age_0 {
  3. void *__isa;
  4. __Block_byref_age_0 *__forwarding;
  5. int __flags;
  6. int __size;
  7. int age;
  8. };
  9. // block结构体
  10. struct __main_block_impl_0 {
  11. struct __block_impl impl;
  12. struct __main_block_desc_0* Desc;
  13. __Block_byref_age_0 *age; // by ref
  14. };

可以观察到,编译器会将block变量包装成一个对象,保存在block的结构体中,结构示意图:
image.png
block封装的变量结构:
image.png
当在block内部修改变量时,就会查找blcok变量,找到内部的val值进行修改,可以通过查看block真正执行的函数main_block_func_0查看:

  1. static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  2. // 找到封装的__blcok变量
  3. __Block_byref_age_0 *age = __cself->age; // bound by ref
  4. // 找到__blcok变量内部保存的age进行修改、打印
  5. (age->__forwarding->age) = 20;
  6. NSLog((NSString *)&__NSConstantStringImpl__var_folders_......,(age->__forwarding->age));
  7. }

至于为什么block变量内部有一个指向自己的指针?block是如何引用block变量的?block变量是如何引用被修饰的变量的?这些问题请参考下一篇文章[block内存管理](https://www.yuque.com/mengxianliang/uuxpmg/aya5l8)。