iOS源码解析:Block的本质

栏目: C++ · 发布时间: 5年前

内容简介: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 这个结构体。

那么我们总结一下:


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

进化

进化

北大首届互联网CIO-CTO班全体同学 著 / 人民邮电出版社 / 2014-10 / 39

精彩视频:http://v.youku.com/v_show/id_XNzkyNzAyNDA0.html 京东购买链接:http://item.jd.com/11549275.html 互动出版网购买链接:http://product.china-pub.com/4352423 互联网是一个年轻的行业,同时也是一个推陈出新、不断进化的行业。 本书是北京大学首届互联网CIO-......一起来看看 《进化》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具