OC笔记 - block工作原理 01:捕获外部变量(基本类型)
block底层实现
1 - 我们先看一个简单的 block实现
① 无返回值无参型 block
1 #import2 int main(int argc, const char * argv[]) { 3 4 @autoreleasepool { 5 6 // 定义 block01 7 void(^block01)(void) = ^{ 8 9 NSLog(@"hello block"); 10 }; 11 12 // 回调 13 block01(); 14 } 15 16 return 0; 17 18 }
② 编译成 C++代码
1 struct __main_block_impl_0 { 2 struct __block_impl impl; 3 struct __main_block_desc_0* Desc; 4 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { 5 impl.isa = &_NSConcreteStackBlock; 6 impl.Flags = flags; 7 impl.FuncPtr = fp; 8 Desc = desc; 9 } 10 }; 11 static void __main_block_func_0(struct __main_block_impl_0 *__cself) { 12 13 14 NSLog((NSString *)&__NSConstantStringImpl__var_folders_t5_d7bd_spn3hsfdqj994fdm9bh0000gn_T_main_bacace_mi_0); 15 } 16 17 static struct __main_block_desc_0 { 18 size_t reserved; 19 size_t Block_size; 20 } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; 21 int main(int argc, const char * argv[]) { 22 23 /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 24 25 void(*block01)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); 26 27 ((void (*)(__block_impl *))((__block_impl *)block01)->FuncPtr)((__block_impl *)block01); 28 } 29 30 return 0; 31 32 }
③ 代码分析
首先我们把 C++代码变成 OC格式
1 int main(int argc, const char * argv[]) { 2 3 /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 4 5 // 指针变量 6 void(*block01)(void) = &__main_block_impl_0(__main_block_func_0, 7 &__main_block_desc_0_DATA 8 ); 9 10 // 执行 block01内部代码 11 block01->FuncPtr(block01);// 指向函数 __main_block_impl_0 12 } 13 14 return 0; 15 16 }
参数 __main_block_func_0
1 // 封装了 block01内部的执行函数 2 static void __main_block_func_0(struct __main_block_impl_0 *__cself) { 3 4 // 这就是 blocl01内部的代码块 NSLog 5 NSLog((NSString *)&__NSConstantStringImpl__var_folders_t5_d7bd_spn3hsfdqj994fdm9bh0000gn_T_main_bacace_mi_0); 6 }
参数 __main_block_desc_0
1 static struct __main_block_desc_0 { 2 3 size_t reserved; // 0 4 size_t Block_size; // 就是 struct __main_block_impl_0的大小 5 } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; // 结构体成员变量赋值
那么这时候回过头来看 __main_block_impl_0函数的功能就一目了然了
1 struct __main_block_impl_0 { 2 struct __block_impl impl; // impl里包含了 block信息:大小、类型、内部执行函数等等 3 struct __main_block_desc_0* Desc; // block大小 4 5 // C++的构造函数(类似 OC中的构造方法),它返回自身结构体__main_block_impl_0 6 // flags=0 默认值是 0,就是说这个参数可以不传 7 __main_block_impl_0(void *fp, 8 struct __main_block_desc_0 *desc, 9 int flags=0) { 10 impl.isa = &_NSConcreteStackBlock;// block是什么类型 11 impl.Flags = flags; 12 impl.FuncPtr = fp; // __main_block_func_0 13 Desc = desc; // block的大小 14 } 15 };
最终关系图示如下
2 - 捕获外部变量(基本类型)
① block02内部使用全局变量、全局静态变量、局部变量、局部静态变量
1 #import2 // 全局变量 3 int pets = 1000; 4 // 全局静态变量 5 static int army = 2000; 6 7 int main(int argc, const char * argv[]) { 8 9 @autoreleasepool { 10 11 // 修饰变量的有三种 auto(自动变量:值传递)、static(静态变量:指针传递)、register(寄存器) 12 // 自动变量:离开作用域就销毁 13 int age = 10;// 默认是 auto,它只存在局部变量中 14 15 // 局部静态变量 16 static int height = 30; 17 18 void(^block02)(void) = ^{ 19 20 NSLog(@"自动变量 is :%d",age); 21 NSLog(@"局部静态变量 is :%d",height); 22 NSLog(@"全局变量 is :%d",pets); 23 NSLog(@"全局静态变量 is :%d",army); 24 }; 25 26 age = 300; 27 height = 500; 28 pets = 20000; 29 army = 30000; 30 block02(); 31 // 自动变量 is :10 32 // 局部静态变量 is :500 33 // 全局变量 is :20000 34 // 全局静态变量 is :30000 35 } 36 37 return 0; 38 39 }
② 编译成 C++(OC格式)
1 // 全局变量不会 block被捕获 2 int pets = 1000; 3 static int army = 2000; 4 5 struct __main_block_impl_0 { 6 7 struct __block_impl impl; 8 struct __main_block_desc_0* Desc; 9 // 局部(静态)变量会被捕获到 block内部 10 int age; // 局部变量:值访问 11 int *height; // 局部静态变量:地址访问 12 // age(_age), height(_height) C++语法就是赋值的意思 age = _age;height = _height 13 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) { 14 impl.isa = &_NSConcreteStackBlock; 15 impl.Flags = flags; 16 impl.FuncPtr = fp; 17 Desc = desc; 18 } 19 }; 20 21 static void __main_block_func_0(struct __main_block_impl_0 *__cself) { 22 23 int age = __cself->age; 24 int *height = __cself->height; 25 26 // 局部变量:捕获值类型 27 NSLog((NSString *)&__NSConstantStringImpl__var_folders_t5_d7bd_spn3hsfdqj994fdm9bh0000gn_T_main_f264bf_mi_0,age); 28 // 局部静态变量:捕获的是地址 29 NSLog((NSString *)&__NSConstantStringImpl__var_folders_t5_d7bd_spn3hsfdqj994fdm9bh0000gn_T_main_f264bf_mi_1,(*height)); 30 // 全局(静态)变量:并会不捕获到 block内部,使用时直接取出使用 31 NSLog((NSString *)&__NSConstantStringImpl__var_folders_t5_d7bd_spn3hsfdqj994fdm9bh0000gn_T_main_f264bf_mi_2,pets); 32 NSLog((NSString *)&__NSConstantStringImpl__var_folders_t5_d7bd_spn3hsfdqj994fdm9bh0000gn_T_main_f264bf_mi_3,army); 33 } 34 35 static struct __main_block_desc_0 { 36 size_t reserved; 37 size_t Block_size; 38 } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; 39 40 41 // main函数 42 int main(int argc, const char * argv[]) { 43 44 /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 45 46 int age = 10; 47 static int height = 30; 48 // 注意传参的不同 49 // 局部变量 age 局部静态变量 &height 50 void(*block02)(void) = &__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &height)); 51 52 age = 300; 53 height = 500; 54 pets = 20000; 55 army = 30000; 56 block02->FuncPtr(block02); 57 } 58 59 return 0; 60 61 }
细节问题
1 - 分析代码,思考其中的三个问题
// - Person.h
1 #import2 @interface Person : NSObject 3 4 @property(nonatomic,copy)NSString *name; 5 -(void)test; 6 @end
// - Person.m
1 #import "Person.h" 2 @implementation Person 3 4 -(void)test{ 5 6 void(^blockA)(void) = ^{ 7 8 // 问题一:block内部会不会捕获 self 9 NSLog(@"%@",self); 10 11 // 问题二:block内部捕获的又是什么 12 NSLog(@"%@",_name); 13 14 // 问题三:block内部捕获的东东又是什么 15 NSLog(@"%@",[self name]); 16 }; 17 18 blockA(); 19 } 20 21 22 @end
结论:不管是问题一还是问题二、问题三,其 block内部捕获的都是局部变量 Person *self
问题一:这个不难理解,block 内部捕获局部变量 Person *self
问题二:访问实例对象的成员变量 _name,其实质是 block内部捕获的变量是 Person *self,通过 self取出成员变量
问题三:访问实例对象的 name方法,同样地 block内部捕获的依旧是 Person *self,通过 self找到它的方法
注:OC中的方法都默认有两个参数 self和 _cmd,既然是参数,那么 self可以理解成一个局部变量,当然可以被 block捕获
结语
1 - block变量捕获机制