iOS底层原理笔记 - Block的变量捕获

408 阅读3分钟

Block的本质

  • block本质上也是一个OC对象,它内部也有个isa指针
  • block是封装了函数调用以及函数调用环境的OC对象
  • 底层结构如图所示

Untitled.png

block的变量捕获

什么是block的变量捕获?

block变量捕获就是在block内部创建一个变量来存放外部变量;

为了保证block内部能够正常访问外部的变量,block有个变量捕获机制

Untitled 1.png

问题一:

以下代码打印出来age是多少?

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        int age = 10;
        void (^block)(void) = ^{
            NSLog(@"age: %d",age);
        };
        age = 20;
        block();

    }
    return 0;
}

答案:age: 10

把以上代码转成c++代码,找到我们的main函数,可以看到block内部对age进行了捕获

Untitled 2.png

在block还没有调用时,block已经对age的值捕获了,并在block内部生成一个age变量,将外部的age的值赋值给了自己的age,此时block内部的age的值已经跟外部没有关联了,所以当外部值改变时并不会影响到内部的值,这种情况称为block的值捕获;

问题二:

以下代码打印出来age和height分别是多少?

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        int age = 10;
        static int height = 10;
        void (^block)(void) = ^{
            NSLog(@"age = %d; height = %d", age, height);
        };
        age = 20;
        height = 20;
        block();

    }
    return 0;
}

答案:age = 10; height = 20

此时多了一个height变量,height是static变量。

继续转成c++代码:

Untitled 3.png

可以看出,block内部分别生成 int age; int *height;

block中存放的是外部height的指针,在运行时block对static局部变量进行的捕获是指针捕获,所以当外部的值改变时block内部的值也会跟着改变;

为什么局部变量age是值传递,而static修饰的局部变量是指针传递呢?

  • 因为局部变量age在离开作用域(大括号)时,就会被释放,如果block是全局的,在其他作用域调用时,如果age是指针传递(保存的是指针),这时block去调用age时,age已经离开作用域被释放了,内存已经不存在了,这是就会存在访问野指针的情况;
  • static修饰的变量其内存会一直存在,不会因为离开作用域而释放,所以当全局block在其他作用域调用时也不会报错;

问题三:

声明两个全局变量,age和height,以下代码打印出来age和height分别是多少?

int age;
int height;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        age = 10;
        height = 10;
        
        void (^block)(void) = ^{
            NSLog(@"age = %d; height = %d", age, height);
        };
        
        age = 20;
        height = 20;

        block();
    }
    return 0;
}

答案:age = 20; height = 20

这次得出结果age和height的值都发生的改变,看代码,这次block内并没有对age和height进行捕获,那么他们的值是如何改变的呢?

Untitled 4.png

原因是age和height都是全局变量,在任何作用域都可以访问,所以block并不需要多此一举进行捕获,在内部直接访问即可。

总结

  1. block只会对局部变量进行捕获,不会对全局变量进行捕获(全局变量直接访问);
  2. 对auto局部变量是值捕获;staic局部变量是指针捕获;