内容简介:Block在iOS开发中的用途非常广,今天我们就来一起探索一下Block的底层结构。下面是一个没有参数和返回值的简单的Block:为了探索Block的底层结构,我们将main.m文件转化为C++的源码、我们打开命令行。cd到包含main.m文件的文件夹,然后输入:
Block在iOS开发中的用途非常广,今天我们就来一起探索一下Block的底层结构。
1. Block的底层结构
下面是一个没有参数和返回值的简单的Block:
int main(int argc, char * argv[]) { @autoreleasepool { void (^block)(void) = ^{ NSLog(@"Hello World!"); }; block(); return 0; } }
为了探索Block的底层结构,我们将main.m文件转化为C++的源码、我们打开命令行。cd到包含main.m文件的文件夹,然后输入: clang -rewrite-objc main.m
,这个时候在该文件夹的目录下会生成main.cpp文件。
这个文件非常长,我们直接拉到文件的最下面,找到main函数:
int main(int argc, char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; //定义block void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); //调用block ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); return 0; } }
这第一行代码是定义一个block变量,第二行代码是调用block。这两行代码看起来非常复杂。但是我们可以去简化一下,怎么简化呢?
变量前面的()一般是做强制类型转换的,比如在调用block这一行, block
前面有一个()是(__block_impl *),这就是进行了一个强制类型转换,将其转换为一个 _block_impl
类型的结构体指针,那像这样的强制类型转换非常妨碍我们理解代码,我们可以暂时将这些强制类型转换去掉,这样可以帮助我们理解代码。
化简后的代码如下:
//定义blockvoid (*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);//调用blockblock->FuncPtr(block);
这样化简后的代码就要清爽多了。我们一句一句的看,先看第一句:
void (*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
这句代码的意思好像就是调用 _main_block_impl_0
这个函数,给这个函数传进两个参数 _main_block_func_0
和 &_main_block_desc_0_DATA
,然后得到这个函数的返回值,取函数返回值的地址,赋值给block这个指针。
我们在稍微上一点的位置可以找到 _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; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
__block_impl这个结构体的结构我们可以command+f在main.cpp文件中搜索得到:
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
_main_block_desc_0结构体的结构在main.cpp文件的最下面可以找到:
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)};
这是一个C++的结构体。而且在这个结构体内还包含一个函数,这个函数的函数名和结构体名称一致,这在 C语言 中是没有的,这是C++特有的。
在C++的结构体包含的函数称为结构体的构造函数,它就相当于是OC中的init方法,用来初始化结构体。OC中的init方法返回的是对象本身,C++的结构体中的构造方法返回的也是结构体对象。
那么我们就知道了, __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
返回的就是 _main_block_impl_0
这个结构体对象,然后取结构体对象的地址赋值给block指针。换句话说,block指向的就是初始化后的 _main_block_impl_0
结构体对象。
我们再看一下初始化 _main_block_impl_0
结构体传进去的参数:
-
第一个参数是
_main_block_func_0
,这个参数的结构在上面一点的位置也能找到:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_74_wk04zv690mz36wn0g18r5nxm0000gn_T_main_3b803f_mi_0); }
这个函数其实就是把我们Block中要执行的代码封装到这个函数内部了。我们可以看到这个函数内部就一行代码,就是一个NSlog函数,这也就是 NSLog(@"Hello World!");
这句代码。
把这个函数指针传给 _main_block_impl_0
的构造函数的第一个参数,然后用这个函数指针去初始化 _main_block_impl_0
这个结构体的第一个成员变量 impl
的成员变量 FuncPtr
。也就是说 FuncPtr
这个指针指向 _main_block_func_0
这个函数。
-
第二个参数是
&_main_block_desc_0_DATA
。我们看一下这个结构:
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)};
在结构体的构造函数中,0赋值给了reserved, sizeof(struct __main_block_impl_0)
是赋值给了Block_size,可以看出这个结构体存放的是 _main_block_impl_0
这个结构体的信息。在 _main_block_impl_0
的构造函数中我们可以看到, _main_block_desc_0
这个结构体的地址被赋值给了 _main_block_impl_0
的第二个成员变量 Desc
这个结构体指针。也就是说Desc这个结构体指针指向 _main_block_desc_0_DATA
这个结构体。
那么我们总结一下:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。