《Objective-C高级编程》读书笔记--2.3.1--Blocks的实质

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

内容简介:Blocks的原理,每当自己对知识体系有一定提升之后,再回过头来看一下曾经读过的书籍,会发现对它的理解逐步加深。借着读书笔记活动,立个小目标,把Block彻底搞明白,重读《Objective-C高级编程 iOS与OS X多线程和内存管理》第二章节block原理部分,一方面给自己做个笔记,另一方面加深以下印象。block代码:执行

Blocks的原理,每当自己对知识体系有一定提升之后,再回过头来看一下曾经读过的书籍,会发现对它的理解逐步加深。借着读书笔记活动,立个小目标,把Block彻底搞明白,重读《Objective-C高级编程 iOS与OS X多线程和内存管理》第二章节block原理部分,一方面给自己做个笔记,另一方面加深以下印象。

block实质

block代码:

void (^blk)(void) = ^ {
        printf("Block");
    };
    blk();
复制代码

执行 xcrun -sdk iphonesimulator clang -rewrite-objc 源代码文件名 就能将含有Block的代码转换为C++的源代码。我是按照书上的示例,同样转换的main.m文件,转换完之后这里就会多出一个 main.cpp 的文件,打开很恐怖, 六万多行...

《Objective-C高级编程》读书笔记--2.3.1--Blocks的实质

实际上和block相关的代码在最后几十行:

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");
    }

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(int argc, char * argv[]) {

    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;
}
复制代码

这就是我们一直在使用的block,因为都是struct结构看上去有点抽象,不过理解起来并不难。

首先先从 __main_block_func_0 函数开始,因为我们想要执行的回调看源码都是写在这个函数里面的,block使用的匿名函数(也就是我们定义的block)实际上被作为简单的 C语言 函数( block__main_block_func_0 )来处理,该函数的参数__cself相当于OC实例方法中指向对象自身的变量self,即__self为指向Block值的变量。__self与OC里面的self相同也是一个结构体指针,是 __main_block_impl_0 结构体的指针,这个结构体声明如下:

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;
  }
};
复制代码

第一个变量是 impl ,也是一个结构体,声明如下:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
复制代码

先看 FuncPrt ,这个就是 block 括号中函数的函数指针,调用它就能执行block括号中的函数,实际上在调用block的时候就是调用的这个函数指针,执行它指向的具体函数实现。 第二个成员变量是 Desc 指针,以下为其 __main_block_desc_0 结构体声明:

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
}
复制代码

其结构为今后版本升级所需的区域和Block的大小。 实际上 __main_block_impl_0 结构体展开最后就是这样:

struct __main_block_impl_0 {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
  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;
  }
复制代码

这就是整个 __main_block_impl_0 结构体所包含的,既然定义了这个结构体的初始化函数,那在详细看一下它的初始化过程,实际上该结构体会像下面这样初始化:

isa = &_NSConcreteStackBlock;
Flags = 0;
Reserved = 0;
FuncPtr = __main_block_func_0;
Desc = &__main_block_desc_0_DATA;
复制代码

__main_block_func_0 这不就是上面说到的那个指向函数实现的那个函数指针,也就是说只需要调用到结构体里面的 FuncPtr 就能调用到我们的具体实现了。那这个构造函数在哪里初始化的,看上面的源码是在我们定义block的时候:

void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
复制代码

简化为:

struct __mian_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0 *blk = &tmp;
复制代码

该源代码将 __mian_block_impl_0 结构体类型的自动变量,即栈上生成的 __mian_block_impl_0 结构体实例的指针,赋值给 __mian_block_impl_0 结构体指针类型的变量blk。听起来有点绕,实际上就是我们最开始定义的 blk__main_block_impl_0 结构体指针指向了 __main_block_impl_0 结构体的实例。

接下来看看 __main_block_impl_0 结构体实例的构造参数:

__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
复制代码

第一个参数为由Block语法转换的C语言函数指针,第二个参数是作为静态全局变量初始化的 __main_block_desc_0 结构体实例指针,以下为 __main_block_desc_0 结构体实例的初始化部分代码:

static struct __main_block_desc_0 __main_block_desc_0_DATA = { 
    0, 
    sizeof(struct __main_block_impl_0)
};
复制代码

__main_block_impl_0 结构体实例的大小。

接下来看看栈上的 __main_block_impl_0 结构体实例( 即Block )是如何根据这些参数进行初始化的。也就是 blk() 的具体实现:

((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
复制代码

简化以下:

(*blk->impl.FuncPtr)(blk);
复制代码

FuncPtr 正是我们初始化 __main_block_desc_0 结构体实例时候传进去的函数指针,这里使用这个函数指针调用了这个函数,正如我们刚才所说的,有block语法转换的__ main_block_func_0 函数的指针被赋值成员变量 FuncPtr 中。 blk 也是作为参数进行传递的,也就是最开始讲到的 __cself 。到此 block 的初始化和调用过程就结束了。

待更新...


以上所述就是小编给大家介绍的《《Objective-C高级编程》读书笔记--2.3.1--Blocks的实质》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

谷歌时代的柏拉图

谷歌时代的柏拉图

[美] 丽贝卡·戈尔茨坦 / 李鹏程 / 中信出版集团·新思文化 / 2017-12-10 / 69.00元

我愿意用我所有的科技去换取和苏格拉底相处的一个下午。 ——史蒂夫•乔布斯 谷歌时代,科技昌明,众声喧哗,哲学提出的许多问题,科学似乎都已经给出了答案。若是如此,为什么我们今天还需要哲学?这个由古希腊城邦时代的哲人苏格拉底和柏拉图开创的学科,真的过时了吗? 已经2400岁 的柏拉图对此有话要说。哲学家兼小说家、美国国家人文奖章获得者戈尔茨坦史海钩沉,从经典著作中复活了柏拉图,让他来......一起来看看 《谷歌时代的柏拉图》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码