1、我们都会使用 block ,但是它真正是如何工作的呢?换句话说block的本质是什么?
看下列实例,想想程序的结果:
int main(int argc, const char * argv[]) {
int a = 10;
__block int b = 20;
void (^block)() = ^(){
printf("a = %d\n",a);
printf("b = %d\n",b);
};
a = 50;
b = 30;
block();
程序的结果是: a = 10 , b = 30;
这个结果可能大家都知道 ,那么为什么会出现这样的情况呢?很多人都会说 ,b前面加了__block所以他的值可以改变 ,a没加所以不能改变 。这只是表象,接下来我们就研究一下他的block的本质。
2、探究block的底层原理,打开终端 ,把目录切换到当前main.m所在的路径下,我们需要把这个 main.m 文件 编程成c++ 的文件 main.cpp.
输入终端命令: clang -rewrite-objc main.m
生成一个main.cpp文件 ,打开它,但是你要做好心理准备,这个 文件十万多行。
3、找到下列代码
int main(int argc, const char * argv[]) {
int a = 10;
//__attribute__((__blocks__(byref))) __Block_byref_b_0 b = {(void*)0,(__Block_byref_b_0 *)&b, 0, sizeof(__Block_byref_b_0), 20};
void (*block)() = &__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a, (__Block_byref_b_0 *)&b, 570425344);
a = 50;
(b.__forwarding->b) = 30;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}}
分析:
3.1、分析这个block参数
&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a, &b, 570425344)
//block -> 实质 函数指针
//__main_block_func_0 指针
//__main_block_desc_0_DATA 地址 block 的描述数据
//a 普通变量
//&b b地址 b -> __Block_byref_b_0类型
//b 变量 就会被封装成 __Block_byref_b_0 结构体类型
3.2、分析struct __Block_byref_b_0 结构体
struct __Block_byref_b_0 {
void *__isa; //当前对象的指针
__Block_byref_b_0 *__forwarding; //当前这种结构体类型的指针 __forwarding存对象自己地址
int __flags; //标记
int __size; //结构体的大小
int b; //变量
};
3.3、函数的初始化
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a; // 10
__Block_byref_b_0 *b; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, __Block_byref_b_0 *_b, int flags=0) : a(_a), b(_b->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp; //执行的那段代码 打印
Desc = desc;
}
}
3.4、__block_impl结构体分析
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
3.5、__main_block_func_0
__main_block_func_0(struct __main_block_impl_0 *__cself) {
//地址 直接访问属性
__Block_byref_b_0 *b = __cself->b; // bound by ref
int a = __cself->a; // bound by copy
printf("a = %d\n",a);
printf("b = %d\n",(b->__forwarding->b));
}
在上述的代码中我们可以理解为: 变量a传入的是值,变量b传入的是地址。__block 把b强转成一个结构体指针。
blcok的本质就是一个函数指针 ,也可以说是一个指向结构体的指针。