HR's Blog

Swimming 🏊 in the sea🌊of code!

0%

Block系列2-block auto和static值捕获源码分析

.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(int argc, const char * argv[]) {
@autoreleasepool {

static int staticValues = 10;
auto int autoValues = 20;
void(^block)(int, int) = ^(int a, int b){
NSLog(@"This is block %d", staticValues);
NSLog(@"This is block %d", autoValues);
NSLog(@"This is block %d", a);
NSLog(@"This is block %d", b);
};
block(50, 60);

staticValues = 30;
autoValues = 40;
}
return 0;
}
1
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o arm64.cpp

编译以后可以看到Block被转换成了如下代码其中__main_block_impl_0应该就是block的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *staticValues; //捕获到static变量是指针
int autoValues; //捕获到auto类型是值拷贝, 也就是说和之前的变量已经脱离关系。
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_staticValues, int _autoValues, int flags=0) : staticValues(_staticValues), autoValues(_autoValues) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
int *staticValues = __cself->staticValues; // bound by copy
int autoValues = __cself->autoValues; // bound by copy

NSLog((NSString *)&__NSConstantStringImpl__var_folders_kw_5dr8tfr96kn9f2mf2nh0111r0000gp_T_main_fca68f_mi_0, (*staticValues));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_kw_5dr8tfr96kn9f2mf2nh0111r0000gp_T_main_fca68f_mi_1, autoValues);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_kw_5dr8tfr96kn9f2mf2nh0111r0000gp_T_main_fca68f_mi_2, a);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_kw_5dr8tfr96kn9f2mf2nh0111r0000gp_T_main_fca68f_mi_3, b);
}

static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;

static int staticValues = 10;
auto int autoValues = 20;
void(*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &staticValues, autoValues));
((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 50, 60);

staticValues = 30;
autoValues = 40;
}
return 0;
}

本质

__main_block_impl_0中第一个参数是一个__block_impl的结构体,而__block_impl第一个参数是isa指针,所以Block的本质就是一个封装了运行环境的OC对象。而且也可以看到外部引用的参数age,也被分装到Block里面了,这也是为什么我们在block里面修改的参数,并不会影响外面的参数。

这里需要注意一点就是staticauto两种修饰符号,block捕获的方式不一样。static的变量,Block是通过指针捕获,因为在作用域结束以后,变量不会销毁。但是auto的变量在作用域结束以后,就会被销毁,所以Block的捕获方式是直接拷贝到结构体实现中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *staticValues;
int autoValues;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_staticValues, int _autoValues, int flags=0) : staticValues(_staticValues), autoValues(_autoValues) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};

struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};

调用

从block最终的实现可以看到结构体__main_block_impl_0初始化的时候传入了__main_block_func_0的参数,调用的时候实质上是调用了__block_implFuncPtr方法。

1
2
3
4
5
6
static int staticValues = 10;
auto int autoValues = 20;

//block实现
void(*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &staticValues, autoValues));
((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 50, 60);