1、局部变量
自动变量:方法内部定义的局部变量默认都有一个auto关键字修饰,特点是离开作用域(大括号)就会自动销毁,且只能在方法内部访问。
- (void)test {
auto int age = 10;
}
静态变量:用static修饰的变量,特点是在程序运行过程中,一只在内存中存在,且只能在方法内部访问。
- (void)test {
static int height = 10;
}
在block中访问auto局部变量age和静态局部变量height:
@autoreleasepool {
int age = 10;
static int height = 10;
void (^block)(void) = ^{
NSLog(@"age = %d, height = %d",age,height);
};
age = 20;
height = 20;
block();
}
打印结果:
~:age = 10, height = 20
分析原因,先转成C++代码:
// block结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;// 值传递
int *height;// 指针传递
};
// block调用方法
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
int *height = __cself->height; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_......,age,(*height));
}
可以观察到:
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:
// 定义了两个全局变量
int age_ = 10;
static int height_ = 10;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block)(void) = ^{
NSLog(@"age = %d, height = %d",age_,height_);
};
age_ = 20;
height_ = 20;
block();
}
return 0;
}
转成C++代码:
// 全局变量
int age_ = 10;
static int height_ = 10;
// block结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
......
};
// block调用方法
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_......,age_,height_);
}
打印结果:
~:age = 20, height = 20
可以观察到:block没有捕获全局变量。
分析:
全局变量可以被程序内所有方法访问,所以block不需要捕获全局变量,所以block外部修改了全局变量会影响block内部调用。
3、self
新建一个Person类,添加一个name属性,添加一个test方法,test方法内部添加一个block,block内部访问name属性:
- (void)test {
void (^block)(void) = ^{
NSLog(@"self.name = %@",self.name);
};
block();
}
转成Person.m转成C++代码后查看block结构体:
// block结构体
struct __Person__test_block_impl_0 {
struct __block_impl impl;
struct __Person__test_block_desc_0* Desc;
Person *self;// 捕获了self
......
};
可以看到block内部捕获了self,之所以会捕获self是因为self是局部变量,通过test方法的C++代码可知,OC方法默认会添加self和_cmd两个参数:
// 默认存在self和_cmd两个参数
static void _I_Person_test(Person * self, SEL _cmd) {
void (*block)(void) = ((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344));
block)->FuncPtr(block);
}
self是当前类的类对象,self是参数也是局部变量,所以在block内部访问成员变量或属性,或者通过self调用方法,都会捕获self。
4、总结
*self是局部变量,所以在block内部访问self,会捕获self。