1、局部变量

自动变量:方法内部定义的局部变量默认都有一个auto关键字修饰,特点是离开作用域(大括号)就会自动销毁,且只能在方法内部访问。

  1. - (void)test {
  2. auto int age = 10;
  3. }

静态变量:用static修饰的变量,特点是在程序运行过程中,一只在内存中存在,且只能在方法内部访问。

  1. - (void)test {
  2. static int height = 10;
  3. }

在block中访问auto局部变量age和静态局部变量height:

  1. @autoreleasepool {
  2. int age = 10;
  3. static int height = 10;
  4. void (^block)(void) = ^{
  5. NSLog(@"age = %d, height = %d",age,height);
  6. };
  7. age = 20;
  8. height = 20;
  9. block();
  10. }

打印结果:

  1. ~:age = 10, height = 20

分析原因,先转成C++代码:

  1. // block结构体
  2. struct __main_block_impl_0 {
  3. struct __block_impl impl;
  4. struct __main_block_desc_0* Desc;
  5. int age;// 值传递
  6. int *height;// 指针传递
  7. };
  8. // block调用方法
  9. static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  10. int age = __cself->age; // bound by copy
  11. int *height = __cself->height; // bound by copy
  12. NSLog((NSString *)&__NSConstantStringImpl__var_folders_......,age,(*height));
  13. }

可以观察到:
block捕获了访问的auto局部变量,且直接捕获了auto变量的值,保存到block的结构体内部。
block捕获了防伪的static局部变量,且捕获的是static变量的指针,保存到block的机构提内部。
分析:
之所以保存了auto变量的值是因为block可能在auto变量的作用域之外执行,避免auto变量自动销毁而访问失败。
之所以保存了static变量的指针式因为static变量一直在内存中存在,所以block无论什么时候执行,都可以访问到static的指针。
所以block外部修改auto局部变量的值不会影响block内部调用,但是block外部修改static局部变量的值会影响block内部调用。

2、全局变量

全局变量:在程序运行过程中,一只在内存中存在,可以被所有方法访问。
在block中访问全局变量age和height

  1. // 定义了两个全局变量
  2. int age_ = 10;
  3. static int height_ = 10;
  4. int main(int argc, const char * argv[]) {
  5. @autoreleasepool {
  6. void (^block)(void) = ^{
  7. NSLog(@"age = %d, height = %d",age_,height_);
  8. };
  9. age_ = 20;
  10. height_ = 20;
  11. block();
  12. }
  13. return 0;
  14. }

转成C++代码:

  1. // 全局变量
  2. int age_ = 10;
  3. static int height_ = 10;
  4. // block结构体
  5. struct __main_block_impl_0 {
  6. struct __block_impl impl;
  7. struct __main_block_desc_0* Desc;
  8. ......
  9. };
  10. // block调用方法
  11. static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  12. NSLog((NSString *)&__NSConstantStringImpl__var_folders_......,age_,height_);
  13. }

打印结果:

  1. ~:age = 20, height = 20

可以观察到:block没有捕获全局变量。
分析:
全局变量可以被程序内所有方法访问,所以block不需要捕获全局变量,所以block外部修改了全局变量会影响block内部调用。

3、self

新建一个Person类,添加一个name属性,添加一个test方法,test方法内部添加一个block,block内部访问name属性:

  1. - (void)test {
  2. void (^block)(void) = ^{
  3. NSLog(@"self.name = %@",self.name);
  4. };
  5. block();
  6. }

转成Person.m转成C++代码后查看block结构体:

  1. // block结构体
  2. struct __Person__test_block_impl_0 {
  3. struct __block_impl impl;
  4. struct __Person__test_block_desc_0* Desc;
  5. Person *self;// 捕获了self
  6. ......
  7. };

可以看到block内部捕获了self,之所以会捕获self是因为self是局部变量,通过test方法的C++代码可知,OC方法默认会添加self和_cmd两个参数:

  1. // 默认存在self和_cmd两个参数
  2. static void _I_Person_test(Person * self, SEL _cmd) {
  3. void (*block)(void) = ((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344));
  4. block)->FuncPtr(block);
  5. }

self是当前类的类对象,self是参数也是局部变量,所以在block内部访问成员变量或属性,或者通过self调用方法,都会捕获self。

4、总结

image.png

*self是局部变量,所以在block内部访问self,会捕获self。