1、变量捕获(1)
- 下面代码打印结果是?
- (void)testBlock {
int age = 10;
void (^block1)(void) = ^(){
NSLog(@"--testBlock--%d",age);
};
age = 20;
block1();
}
- 我们先不着急公布答案,我们可以来看看c++代码是怎么样的。
- 转换成c++代码方式请看 这里
static void _I_ViewController_testBlock(ViewController * self, SEL _cmd) {
int age = 10;
// 这里的第三个参数,age:是值传递,相当于把10传递进去了
void (*block1)(void) = ((void (*)())&__ViewController__testBlock_block_impl_0((void *)__ViewController__testBlock_block_func_0, &__ViewController__testBlock_block_desc_0_DATA, age));
age = 20;
((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
}
// 再看下面这段代码,:age(_age)是c++语法,意思是把参数直接赋值给了int age;
// 所以,这个时候,block内部捕获的age值是10了
struct __ViewController__testBlock_block_impl_0 {
struct __block_impl impl;
struct __ViewController__testBlock_block_desc_0* Desc;
int age;
__ViewController__testBlock_block_impl_0(void *fp, struct __ViewController__testBlock_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 再看最后实现方法
// int age的值就是从__ViewController__testBlock_block_impl_0取出来的
// 打印结果就会是10了
// 而且 int age = __cself->age; // bound by copy
// 写了是copy的意思,就是复制过来的了
static void __ViewController__testBlock_block_func_0(struct __ViewController__testBlock_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_rxjvg1rx6hs778r_253ckdt40000gn_T_ViewController_fbfb1a_mi_0,age);
}
打印结果:–testBlock–10
2、变量捕获(2)
- 那么我们接下来再看下另外一种类似的情况,带有__block修饰
- (void)testBlock {
__block int age = 10;
void (^block1)(void) = ^(){
NSLog(@"--testBlock--%d",age);
};
age = 20;
block1();
}
- 同样的,我们还是先来一起看看C++代码的实现
// 可以看到,我们的age这次不是值传递哦,而是带&,这是地址传递了
static void _I_ViewController_testBlock(ViewController * self, SEL _cmd) {
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
void (*block1)(void) = ((void (*)())&__ViewController__testBlock_block_impl_0((void *)__ViewController__testBlock_block_func_0, &__ViewController__testBlock_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
(age.__forwarding->age) = 20;
((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
}
// 注意到,这次的age是引用的方式了,ref
// age(_age->__forwarding)虽然对C++语法不是很熟,但是我们猜测得到,这是指向age的地址的意思
struct __ViewController__testBlock_block_impl_0 {
struct __block_impl impl;
struct __ViewController__testBlock_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
__ViewController__testBlock_block_impl_0(void *fp, struct __ViewController__testBlock_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 再看这里
// __Block_byref_age_0 *age = __cself->age; // bound by ref
// ref 就是引用的意思,还是引用age的地址,而不是直接复制值
static void __ViewController__testBlock_block_func_0(struct __ViewController__testBlock_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_rxjvg1rx6hs778r_253ckdt40000gn_T_ViewController_1e09b7_mi_0,(age->__forwarding->age));
}
所以,我们可以大胆猜测得到,block内部的值被修改了
实际上打印的结果:–testBlock–20
证实了我们的猜测!
3、变量捕获(3)
- (void)testBlock {
// auto :局部变量的默认修饰关键字
auto int weight = 1;
__block int age = 2;
static int height = 3;
void (^block1)(void) = ^(){
NSLog(@"--testBlock--%d--%d--%d",weight,age,height);
};
weight = 4;
age = 5;
height = 6;
block1();
}
- C++代码
static void _I_ViewController_testBlock(ViewController * self, SEL _cmd) {
auto int weight = 1;
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 2};
static int height = 3;
// 这里可以看到,带&的就是指针传递
void (*block1)(void) = ((void (*)())&__ViewController__testBlock_block_impl_0((void *)__ViewController__testBlock_block_func_0, &__ViewController__testBlock_block_desc_0_DATA, weight, &height, (__Block_byref_age_0 *)&age, 570425344));
weight = 4;
(age.__forwarding->age) = 5;
height = 6;
((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
}
- 同理可以猜出,带有&的,会被修改值
打印结果: --testBlock–1--5–6
也是符合我们的猜想的
4、变量捕获(4)
- 代码再改下
int ageGlobal = 10;
static int heightGlobal = 10;
- (void)viewDidLoad {
[super viewDidLoad];
[self testBlock];
}
- (void)testBlock {
void (^block1)(void) = ^(){
NSLog(@"--testBlock--%d--%d",ageGlobal,heightGlobal);
};
ageGlobal = 20;
heightGlobal = 20;
block1();
}
- 转C++代码
// 这次怎么没有传递ageGlobal 和 heightGlobal的值或者是指针呢?
static void _I_ViewController_testBlock(ViewController * self, SEL _cmd) {
void (*block1)(void) = ((void (*)())&__ViewController__testBlock_block_impl_0((void *)__ViewController__testBlock_block_func_0, &__ViewController__testBlock_block_desc_0_DATA));
ageGlobal = 20;
heightGlobal = 20;
((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
}
- 再看这部分代码,发现也是全局变量
- C++代码也是直接访问全局变量,所以没必要引用或传递值了
int ageGlobal = 10;
static int heightGlobal = 10;
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("testBlock"));
}
struct __ViewController__testBlock_block_impl_0 {
struct __block_impl impl;
struct __ViewController__testBlock_block_desc_0* Desc;
__ViewController__testBlock_block_impl_0(void *fp, struct __ViewController__testBlock_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __ViewController__testBlock_block_func_0(struct __ViewController__testBlock_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_rxjvg1rx6hs778r_253ckdt40000gn_T_ViewController_1c9270_mi_0,ageGlobal,heightGlobal);
}
最后打印结果:–testBlock–20–20
5、再看一个
- (void)testBlock {
void (^block1)(void) = ^(){
NSLog(@"%@",self);
};
block1();
}
这个会不会self,会不会被捕获?
答案是会,因为self是参数
在OC里面,每一个函数调用有两个默认的参数
- (void)testBlock(self, (SEL)sel){
}
既然self是参数,参数就是局部变量了,局部变量就会被捕获。
6、总结
1、局部变量会被捕获,全局变量不会被捕获
2、static 修饰的或者是__block修饰的了,是指针传递
3、auto 修饰的,是值传递