什么是block
block就是包含了函数和函数参数的一个结构体(也可以理解为对象).
Block可以拆分为三大块内容,
- block内容生成的函数(本质就是函数调用),
- block结构体,
- 还有__block结构体(使用__block修饰后生成的结构体)
block的具体实现原理:
1.根据代码动态生成结构体
__Block_byref_age_0
__main_block_impl_0
2.block的内容动态生成函数
__main_block_func_0
3.main函数中__block生成__block对象 --- age
3.将age对象,函数地址传递给block结构体构造函数,生成Block结构体 --- block;
4.在执行block时候,调用block->funcPtr执行函数(这里传入block,验证了block包含了函数和函数调用所需参数)
例子
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 10;
MJBlock block = ^{
age = 30;
};
block();
}
return 0;
}
转换成c++后:
//__block修饰参数结构体
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
//Block结构体,这是一个c++结构体,内部这个函数是构造函数,调用后返回结构体(对照OC init方法)
struct __main_block_impl_0 {
struct __block_impl impl; //函数
struct __main_block_desc_0* Desc; //相关描述(以及Block结构体的copy及释放方法,暂时不做具体分析)
__Block_byref_age_0 *age; //__block修饰的参数
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp; //这里将函数地址赋值给ptr属性
Desc = desc;
}
};
//block包含内容实际是个函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // block存储的__block对象(结构体)
(age->__forwarding->age) = 30; //使用__block对象内部指针__forwarding指向真是的__block对象,然后取出变量,进行赋值
/*
(这里使用__forwarding的原理有点混乱,
有一种说法是block copy的时候__block也会从栈上copy到堆中,这里__forwarding就会指向堆中的__block对象,
但是同时这个堆中的__block对象会赋值给堆中的block,所以取出的age就应该是堆中的__block age,
无需在使用 __forwarding指针指向自己,
所以这里这样使用真是目的还需要进一步探索)
*/
}
//main函数本身
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__Block_byref_age_0 age = {
0,
&age, //age结构体对象本身的地址
0,
sizeof(__Block_byref_age_0),
10 //外部变量
};
MJBlock block = &__main_block_impl_0(
__main_block_func_0, //block内容封装的函数
&__main_block_desc_0_DATA, //block本身的功能(包含block本身的copy和释放的逻辑)
&age, //__block结构体
570425344
);
block->FuncPtr(block); //执行block
}
return 0;
}
以上是关于block以及__block的实现原理的个人理解,要把它想象成为一个包裹,包含着函数imp和调用环境(参数等).
细节
1.ARC中block会自动从栈中copy到堆中,MRC中不会,需要手动copy;
2.使用__block的原因,为什么要使用__block修饰呢?这里说ARC下:是为了让普通的局部变量
(基本数据类型)可以在block内部,也就是函数中使用,看了上边的例子,大家就可以理解为什么要这样做了,如果不用__block将局部的变量包含起来,一旦出了作用域,age马上便释放了,所以ARC中使用__block是为了,让其可以在接下来的函数中继续使用;
3.__block修饰的变量,在生成__block结构体时,如果在MRC下不会进行retain操作,可以理解为弱引用,可以用来解决MRC下的循环引用问题,但是在ARC下会对引用的对象进行retain操作,强引用,这里可以使用__weak进一步修饰,出现了如同
__block __weak Person *weakPerson = person;
的方式,但是对于对象,我们不需要__block修饰了,因为对象本身就在堆中,可以用指针的方式获取到,所以不用__block修饰,最后就是我们常见的
__weak Person *weakPerson = person;
或者__weak typeof(person) weakPerson = person;
4.一般面试中会问你__block和__weak的区别,其实本身他们的作用就是不一样的,__block意义在于对局部变量的持久捕获(个人词汇...),让其在block调用的时候依然存在,而__weak是在ARC下,表示对对象的捕获为弱捕获(个人词汇...),不对其引用计数产生影响;
网友评论