1、Block是什么
block是一个封装了函数调用以及函数调用环境的OC对象(内部也有一个isa指针)
2、底层探究
示例代码,在main.m中简单创建一个block,然后转化为C/C++代码,研究其结构、原理
int main(int argc, const char * argv[]) {
void (^block)(void) = ^(){
NSLog(@"block");
};
block();
return 0;
}
打开终端执行xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
,会生成一个main.cpp文件,接下来大部分时间都是研究这个文件。其中blcok 的代码转化为下面代码
通过
clang
命令行工具中的-rewrite-objc
参数,可以把OC代码转化为C++的实现
int main(int argc, const char * argv[]) {
//blcok创建
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
//调用
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
上面代码的意思就是调用__ main _ block _ impl _ 0()
函数,并且给该函数传递__ main _ block _ func_ 0
和__ main _ block _ desc _ 0_DATA
参数,构建blcok,也就是取__ main _ block _ impl _ 0()
函数的返回值,赋值给block指针。之后在调用这个block。
上面涉及了三种结构,分别是__ main _ block _ impl _ 0()
、__ main _ block _ func _ 0
、__ main _ block_ desc_0_DATA
1)__ main _ block _ impl _ 0()
struct __main_block_impl_0
{
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// 构造函数(类似于OC的init方法),返回结构体对象
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0)
{
impl.isa = &_NSConcreteStackBlock;//表示block类型
impl.Flags = flags;
impl.FuncPtr = fp;//blcok内代码块地址
Desc = desc;//存储block对象占用内存大小
}
};
//Block对象结构
struct __block_impl {
void *isa;
int Flags;//用于按位表示一些block附加信息
int Reserved;//保留变量
void *FuncPtr;//存储了执行 block 代码块的函数地址
};
我们可以看到__ main _ block _ impl _ 0()
是一个结构体,__ main _ block _ impl _ 0内部有一个构造函数,在构造函数内部使用FuncPtr记录封装要执行代码的地址(__ main_block_func_0地址),使用Desc记录blcok占用内存大小,除了有构造函数还有两个成员变量,分别是
- __ block_impl impl:__ block_impl是__ main_block_impl_0结构体的第一个成员,相当于将__ block_impl结构体的成员直接拿出来放在__ main_block_impl_0中,那么也就说明__ block_impl的内存地址就是__main_block_impl_0结构体的内存地址开头,这也是在block调用时,使用成员变量获取FuncPtr的值,来实现函数调用的原因
- __main_block_desc_0* Desc:方便获取block占用内存大小。
了解了__main_block_impl_0结构之后,我们再来分析一下block的创建。
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
block的创建也就是通过参数初始化__ main _ block _ impl _ 0()
结构,并且将初始化后的结构体地址赋值给block指针,而参数__ main _ block _ func_ 0
记录了block内部代码块的地址,而__ main _ block _ desc _ 0_DATA
参数记录了对象的相关信息,例如block对象占用内存的大小。
下面我们分别看看这两个参数
- __main_block_func_0
// 封装了block执行逻辑的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_yl_5wf9qyc526sfkg_phlkp9r2r0000gp_T_main_2f0d21_mi_0);
}
我们可以看到一个NSLog,这正是我们在blcok中写的代码,由此可以总结出我们写在block块中的代码封装到__main_ block_func_0函数内部了,并将__ main_block_func_0函数的地址作为*fp传入了__main_block_impl_0的构造函数中保存在结构体内FuncPtr变量中,也就是FuncPtr指针指向block代码块。
3)__main_block_desc_0_DATA
其结构是__