Block的本质
- block本质上也是一个OC对象,它内部也有个isa指针
- block是封装了函数调用以及函数调用环境的OC对象
- 底层结构如图所示
block的变量捕获
什么是block的变量捕获?
block变量捕获就是在block内部创建一个变量来存放外部变量;
为了保证block内部能够正常访问外部的变量,block有个变量捕获机制
问题一:
以下代码打印出来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进行了捕获
在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++代码:
可以看出,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进行捕获,那么他们的值是如何改变的呢?
原因是age和height都是全局变量,在任何作用域都可以访问,所以block并不需要多此一举进行捕获,在内部直接访问即可。
总结
- block只会对局部变量进行捕获,不会对全局变量进行捕获(全局变量直接访问);
- 对auto局部变量是值捕获;staic局部变量是指针捕获;