2015笔记——Block探究
自己根据步骤进行了Block的实现的本质探究:
(一)
首先来看最简单的仅仅打印一句话的block:

转为C++的部分文件为:

从第一个结构体的命名可以看出这是block的实现,并且得知block在Clang编译器前端得到实现,可以生成C中间代码。

第一部分:
block的实现结构体: 1、block的实现
2、block的描述信息结构体
3、block的构造函数
block的构造函数中:包含了初始化block的类型、初始化block的函数指针。
注:NSConcreteStackBlock意为block位于栈中。
第二部分:
block的函数体:接收一个__cself的参数,即对应的block自身。
第三部分:
block的描述信息结构体:含有block的大小
第四部分:
main函数:block的创建和调用。
创建:将block的函数体和block的描述信息传给构造函数。
调用:就是调用一个以block自身作为参数的函数,该函数为block的函数体。
在将block作为回调函数传递给底层框架时,底层框架需要对其copy一份。我们通常将block写在栈中,需要回调时,往往回调block已经不在栈中了,使用copy属性可以将block放在堆中。
(二)
带有变量的block的实现:

转为C++ 之后的部分文件为:

第一部分:多了一个成员变量int型i,用来存储使用到的局部变量i。构造函数中,多了一个表达式 i(_i),这是一个赋值的过程。
第二部分:从此处可以看出,__cself参数的作用,类似于OC中的self。
第三部分:基本上无变化。
第四部分:在创建block时,多传入一个int型的参数i,值为1024。
因为main函数(第四部分)中的局部变量i和函数__main_block_func_0(第二部分)不在同一个作用域中,调用过程中只是进行了值传递。在上面代码中,我们可以通过指针来实现局部变量的修改,前提是在调用__main_block_func_0时,main函数栈还没展开完成(main函数还未执行完,即block在main函数创建完后,立即调用),变量i还在栈中。但是在很多情况下,block是作为参数传递以供后续回调执行的。通常在这些情况下,block被执行时,定义时所在的函数栈已经被展开,局部变量已经不在栈中了(block此时被copy到堆中),再用指针访问就会崩溃。
(三)
变量前有__block指示符,block的创建和调用:

转为C++之后的部分文件:


第一部分:
block变量对应的结构体,由第一个成员__isa可知,__Block_byref_i_0也可以是NSObject对象。
第二个成员变量指向自己,但指向自己是没有意义的,所以其有时需要指向另一个__Block_byref_i_0结构。
最后一个变量是目标存储变量i。
第二部分:
__main_block_impl_0的成员变量i变成了__Block_byref_i_0类型的一个变量。
第三部分:
block的函数体中,__Block_byref_i_0指针类型变量i,通过其成员变量__forwarding指针来操作另一个成员变量。
第六部分:
block的描述信息中多了两个成员函数,对应于第四部分和第五部分。
第四部分的功能为:当block从栈上拷贝到堆上时,该函数将__Block_byref_i_0类型的成员变量i从栈上复制到堆上。
第五部分的功能为:当block释放时,该函数释放__Block_byref_i_0类型的成员变量i。
此外,如果栈上和堆上同时对该变量进行操作,则_forwarding的作用就体现出来了:当一个__block变量从栈上被复制到堆上时,栈上的__Block_byref_i_0结构体中的__forwarding指针也会指向堆上的结构。
第七部分:
main中先构建了一个__Block_byref_i_0型的i,并设置了__forwarding -> &i 以及i的初始值1024。
构建myBlock时,向构造函数中传入了函数体,描述信息,&i。
总的概括:
带有__Block 指示符的变量i,block中会多出一个结构体,该结构体中有一个_forwarding指针,通过该指针可以操作成员变量i。而且当一个栈上的__Block_byref_i_0变量复制到了堆上,则栈上的__Block_byref_i_0结构体中的_forwarding指针会指向堆上的结构。
此外,block的描述结构体中多出两个成员函数,当block发生拷贝和释放时,对__block类型的成员变量i进行拷贝与释放。
附图一张:

加油~