Block底层结构分析
定义
block 是封装了函数调用及函数调用环境的OC对象
源码
//全局静态变量
static NSString* name_ = @"你好";
//全局自动变量
double height_ = 175.0;
int main(int argc, const char * argv[]) {
@autoreleasepool {
//局部静态变量
static int age = 10;
//局部自动变量
NSString *gender = @"男";
double (^block)(int, int) = ^(int a, int b) {
NSLog(@"局部变量:%@",gender);
NSLog(@"局部静态变量:%d",age);
NSLog(@"全局静态变量:%@",name_);
NSLog(@"全局自动变量:%.f",height_);
return a * b * age * height_;
};
int c = block(10, 10);
}
return 0;
}
clang指令后的cpp文件代码
通过代码可以看到,block实质上是一个由__main_block_impl_0生成的结构体对象指向的地址。
block是通过该地址的funcPtr指向的__main_block_func_0来调用;
__main_block_impl_0 构造函数中对应的参数分别是:
- __main_block_func_0,
- __main_block_desc_0_DATA,
- gender
- (__Block_byref_age_0 *)&age,
- 570425344
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
static int age = 10;
NSString *gender = (NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_3c5203_mi_1;
double (*block)(int, int) = ((double (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, gender, &age, 570425344));
int c = ((double (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10);
}
return 0;
}
__main_block_impl_0结构体
可以发现 全局静态变量name_和全局自动变量 height_ 没有被捕获,该结构体中被捕获的成员有局部自动变量gender和局部静态变量age
//全局静态变量不截获
static NSString* name_ = (NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_3c5203_mi_0;
//全局自动变量不截获
double height_ = 175.0;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//局部自动变量截获
NSString *gender;
//局部静态变量截获
int *age;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSString *_gender, int *_age, int flags=0) : gender(_gender), age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
__block_impl 结构体
从结构体中能发现isa指针,说明block本质上就是OC对象
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
__main_block_func_0 方法
观察__main_block_func_0方法,其中,gender和age都是通过__cself指针来获取,其中__cself的类型为__main_block_impl_0
- FuncPtr 函数调用方法
- copy 方法
- dispose 方法
static double __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
NSString *gender = __cself->gender; // bound by copy
int *age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_3c5203_mi_2,gender);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_3c5203_mi_3,(*age));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_3c5203_mi_4,name_);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_3c5203_mi_5,height_);
return a * b * (*age) * height_;
}
__main_block_desc_0 结构体
- 保留字段
- blockSize
- copy方法
- dispose方法
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->gender, (void*)src->gender, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->gender, 3/*BLOCK_FIELD_IS_OBJECT*/);}
__block修饰后
__main_block_impl_0 结构体成员中多了一个__Block_byref_sex_0 类型的sex
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSString *gender;
int *age;
__Block_byref_sex_0 *sex; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSString *_gender, int *_age, __Block_byref_sex_0 *_sex, int flags=0) : gender(_gender), age(_age), sex(_sex->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static double __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
__Block_byref_sex_0 *sex = __cself->sex; // bound by ref
NSString *gender = __cself->gender; // bound by copy
int *age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_4c84f9_mi_2,gender);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_4c84f9_mi_3,(*age));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_4c84f9_mi_4,name_);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_4c84f9_mi_5,height_);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_4c84f9_mi_6,(sex->__forwarding->sex));
return a * b * (*age) * height_;
}
__Block_byref_sex_0结构体
注意该结构体中的__forwarding指针
struct __Block_byref_sex_0 {
void *__isa;
__Block_byref_sex_0 *__forwarding;
int __flags;
int __size;
int sex;
};
由__main_block_func_0中(sex->__forwarding->sex) sex的取值方式
可以知道通过__block 修饰过的局部变量,会生成一个__Block_byref_sex_0结构体,其中的__forwarding指针指向结构体自身
如果是栈上的block,取值通过forwading指针找到对应的__Block_byref_sex_0 结构体 取出对应的sex变量
如果是堆上的block,forwarding指针会指向堆上的block,而堆上的block自身的__forwarding指针找到对应的__Block_byref_sex_0 结构体 取出对应的sex变量
这样能保证无论在堆上还是在栈上,通过__block修饰过的变量,通过__forwarding指针总能被正确的找到
static double __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
__Block_byref_sex_0 *sex = __cself->sex; // bound by ref
NSString *gender = __cself->gender; // bound by copy
int *age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_4c84f9_mi_2,gender);
NSLog((NSString*)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_4c84f9_mi_3,(*age));
NSLog((NSString
*)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_4c84f9_mi_4,name_);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_4c84f9_mi_5,height_);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rz_d1jw96h527v_b9sn9cq2yszr0000gn_T_main_4c84f9_mi_6,(sex->__forwarding->sex));
return a * b * (*age) * height_;
}