美文网首页
Block由浅入深(2):Block是一个对象

Block由浅入深(2):Block是一个对象

作者: LordLamb | 来源:发表于2017-12-06 14:44 被阅读0次

苹果的官方文档将Block描述为具有C语言级的语法,同时有运行时特性的对象,参见Blocks Programming Topics Introduction。为什么说Block是一个对象呢,我们可以从Block的内部实现中得到一定的启示。

为了分析Block的内部实现,我们需要将Block语法转化为我们可读的源代码,我们可以使用clang命令的-rewrite-objc选项将Block语法转化为C++代码。

  1. clang是基于LLVM的C语言族编译器,可以编译C、C++、Objective-C/C++等语言。
  2. 命令是clang -rewrite-objc ${source_file}.m,会生成同名的.cpp文件。

我们先从最简单都Block开始:

int main()
{
    void (^blk)(void) = ^{printf("Block\n");};

    blk();

    return 0;
}

使用clang -rewrite-objc转化后的关键代码如下:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __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;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");}

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)};
int main()
{
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);

    return 0;
}

因为C++中struct和class有相似的语义,所以转化后的代码struct我们可以全部认为是class。
我们首先看main函数,第一行代码对应着源代码中的第三行,我们再做一些精简:

struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0 *blk = &tmp;

这里首先调用了__main_block_impl_0的构造函数,然后将生成的对象指针传递给了blk变量。
__main_block_impl_0包含了两部分数据:__block_impl是Block的实现部分,__main_block_desc_0是Block的描述。
__main_block_impl_0构造函数中我们可以看到,它第一个参数是一个void类型的指针,void类型的指针类似于Objective-C中的id,理论上可以传递任何数据。它的第二个参数是一个__main_block_desc_0类型的结构体,我们可以找到这个结构体的定义,大概可以得出这个结构体是用来描述这个block的大小的。
我们回到main函数的第一行,这里调用__main_block_impl_0的构造函数时提供的第一个参数是一个名为__main_block_func_0的静态函数指针,而__main_block_func_0的实现正是源代码中Block体的实现。
__main_block_impl_0构造函数的第二个参数是一个名为__main_block_desc_0_DATA的变量,我们可以看到这个变量刚好是描述这个Block对象大小的。

我们接着看main函数,同样我们做一些精简:

__block_impl *blkTmp = (__block_impl *)blk;
blkTmp->FuncPtr(blk);

这里就是一个简单的通过函数指针调用函数,从前面分析得知,被调用的就是__main_block_func_0函数。

这个例子中,我们跳过了两个重要的信息,一个是__block_impl的isa变量,在这个例子中,isa变量被赋值为&_NSConcreteStackBlock,在GNUStep开源代码中,我们可以找到这个变量的定义:

struct objc_class _NSConcreteGlobalBlock;
struct objc_class _NSConcreteStackBlock;
struct objc_class _NSConcreteMallocBlock;

我们知道每一个Objective-C的对象,在内部实现中都有一个isa指针,指向一个struct objc_class的结构表明这个对象是什么类。所以我们可以得到类似的结论:上面的Block是一个_NSConcreteStackBlock对象。

关于_NSConcreteGlobalBlock_NSConcreteMallocBlock对象我们后面再详细解释。

另外一个重要的信息是__main_block_func_0的参数中有一个__cself(我们可以理解为self),这个变量在这个函数中虽然没有使用到,但是这个变量有重要作用,通过它我们可以访问到Block内部的任何变量,我们将在后续的例子中说明。

相关文章

  • Block由浅入深(2):Block是一个对象

    苹果的官方文档将Block描述为具有C语言级的语法,同时有运行时特性的对象,参见Blocks Programmin...

  • Block由浅入深(5):三种类型的Block

    Block的三个类型 在本系列由浅入深(2)我们说到Block是一个对象,它有三种不同的类型,三个类型的定义如下:...

  • Block

    参考文献 Block本质Block由浅入深(5):三种类型的BlockBlock由浅入深(3):Block捕获局部...

  • iOS __block和__weak的区别

    __block 1.__block对象在block中是可以被修改、重新赋值的。2.__block对象在block中...

  • Block 跳转页面A->B->C C->

    1,首先定义代理(定义block)。 2触发代理()触发block, 3,执行代理的对象(执行block的对象) ...

  • Block 由浅入深

    Block是对象。 如何判断mrc,arc 1.delloc 能否调用super,能mrc,不能arc。 2.re...

  • Block

    Block概念介绍 block是一段代码块,是一个对象 Block的声明 Block的定义 Block的快捷定义:...

  • iOS底层day6 - 探索block

    什么是block 1.block本身也是一个OC对象,它里面也有isa指针2.block是封装了函数调用(存储函数...

  • objective-C Block对象

    block对象简介及语法 什么是block? block对象是一组指令,可以像调用函数指令那样调用block对象。...

  • Block 相关知识

    1、什么是block 2、block截获对象 3、在什么情况下需要使用__block修饰符? 4、为什么block...

网友评论

      本文标题:Block由浅入深(2):Block是一个对象

      本文链接:https://www.haomeiwen.com/subject/mkakixtx.html