OC中的Block
2020-07-17 本文已影响0人
it小小菜鸟
Block的本质
- Block本质上也是一个OC对象,它内部也有isa指针
- Block是封装了函数调用(地址)以及函数调用环境(参数、外部变量)的OC对象
- 结构如图下图 截屏2020-07-16 下午4.28.57.png
block的变量捕获(capture)
为了保证block内部能够正常访问外部的变量,block有个变量捕获机制:就是在结构体生成时,会在结构体中增加变量,并且把外部的值赋值给结构体中生成的变量。
// 捕获age变量后的结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
为什么需要需要捕获,主要变量作用域的问题,等于跨函数访问变量
截屏2020-07-16 下午5.40.46.png
Block的类型
Block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型,继承关系如下
_ NSGlobalBlock _ : _ _NSGlobalBlock - : NSBlock - NSObject
- __NSGlobalBlock__ (_NSConcreteGlobalBlock)
- __NSMallocBlock__ (_NSConcreteMallocBlock)
-
__NSStackBlock__ (_NSConcreteStackBlock)
截屏2020-07-17 上午10.53.39.png
每一种block调用copy后的结果如下:
截屏2020-07-17 上午11.12.37.png
内存布局如下图:
- text段:放代码的
- data段:一般放全局变量
- 堆段:放alloc生成的对象,malloc申请的内存。动态分配内存,需要自己申请,管理内存
- 栈段:放局部变量
Block的copy
- 在ARC环境下,编译器会根据情况自动将栈上的block复制(copy)到堆上。比如以下情况:
- block作为函数返回值时
- 将block赋值给强__strong 指针时
- block作为Cocoa API中方法名含有usingBlock的方法参数时
- block作为GCD API的方法参数时
Block访问对象类型的Auto变量时:
block的__main_block_desc_0
结构体,会生成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*);
}
- 如果block是在栈上,将不会对auto变量产生强引用
- 如果block被拷贝到堆上
- 会调用block内部的copy函数
- copy函数内部会调用
_Block_objct_assing
函数_Block_objct_assing
函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained
)做出相应的操作,形成强引用、弱应用- 如果block从堆中移除
- 会调用block内部的dispose函数
- dispose函数内部会调用
_Block_objct_dispose
函数_Block_objct_dispose
函数会自动释放对auto变量的引用
截屏2020-07-17 下午4.12.47.png
Block底层结构
截屏2020-07-17 上午10.10.48.png
-
底层结构体:
// 没有外部变量的
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 捕获age变量后的结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// struct __block_impl 结构体的定义
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
把代码转车c++后,分析如下
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// auto :自动变量,离开作用域就销毁 ;在block捕获的时候,传递的是值
// static:静态变量;在block捕获的时候,传递的是地址
int age;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 封装了block执行逻辑的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
int age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_05_w32rksls5xzd0gy2v5cbgnmm0000gn_T_main_a2ac46_mi_0);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_05_w32rksls5xzd0gy2v5cbgnmm0000gn_T_main_a2ac46_mi_1,age);
}
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;
int age = 10;
// 定义block变量
void (*block)(int, int) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA,
age
);
/* __main_block_impl_0 函数调用完,会返回 __main_block_impl_0 结构体。
所以
void (*block)(int, int) = &结构体;
*/
// 执行block内部的代码
(struct __block_imp1 *)block->FuncPtr(block, 3, 4);
// 因为 impl 是 __main_block_impl_0 的第一个元素,所以 imp1的地址就是 __main_block_impl_0 。所以能强制转换成 struct __block_imp1
}
return 0;
}