Block实现-block的实质

Posted 故园的梨花

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Block实现-block的实质相关的知识,希望对你有一定的参考价值。

Block的实质:

#import <Foundation/Foundation.h>
int main() {
    void(^blk)(void)=^{printf("Block");};
    blk();
    return 0;
}

使用clang将该block转换。

clang -rewrite-objc  源代码文件

转换后形成的block文件是:

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


 


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;


}



下面我们就来具体分析一下这些源代码:

首先我们看到

^{printf("Block");};

转换后称为

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block");}

如上所示的,block使用的匿名函数,其实被转换成c语言函数处理,,该函数中 的_cself是_main_block_func_0结构体的指针。

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;

具有两个成员变量,_block_impld 代码如下:

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

第二个成员变量是

__main_block_desc_0:其代码如下:


static struct __main_block_desc_0 {

  size_t reserved;

  size_t Block_size;

}


这个包含了今后版本升级所需的区域以及block的大小。

下面我们来看一下初始化含有这些数据的结构体的代码:

__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;
  }

在这里面NSConcreteStackBlock用来初始化,接下来我们对构造函数的调用:

 void(*blk)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

因为转换太多实在太难以理解,所以我们去掉转换的部分,这样得到的结果是:

struct _main_block_impl_0 tmp=_main_block_impl_0(_main_block_func_0,&_main_block_desc_DATA);
struct _main_block_impl_0  *blk=&tmp;

这样一来我们可以清楚的看到,

void(^blk)(void)=^{printf("Block");};

这个语句的实现了_main_block_impl_0的两个变量,一个功能函数,一个desc,将block语法生成的block类型对象再赋值给blk,相当于将实例的指针赋值给blk,_main_block_desc_DATA主要是对_main_block_impl_0的结构体实例的大小进行初始化.

blk();

这部分代码转换成为了

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

我们一样去掉转换的部分得到:

(*blk)->impl.Funcptr(blk)

这其实是最简单的函数指针调用函数,正如我们刚才确认的那样,在调用函数中我们看出block是作为参数进行传递的。

那么到底什么是NSConcreteStackBlock呢?

为了理解他首先我们需要理解oc类和对象的实质,其实,所谓的block就是对象,“id”这一变量用于存储oc对象,NSConcreteStackBlock就相当于c结构体实例,将block作为oc对象处理时,该类的信息就放置于NSConcreteStackBlock处。

以上是关于Block实现-block的实质的主要内容,如果未能解决你的问题,请参考以下文章

ios block和delegate的区别

傻瓜学编程之block_2

Objective-C Block与函数指针比较

Blocks的实质学习总结

Objective-C 基础之— Block本质+源码剖析

iOS学习之代码块(Block)