Objective-C Block(闭包)实现

Block的实质

Block我想写OC的都不会陌生,在项目中我们经常会使用block作为数据处理后的回调,例如通知主线程UI更新等。Block的语法看上去很特别,但是它实际上是作为及普通的C源代码来处理的。含有Block的源代码转换为一般的C语言代码,之后作为C语言代码被编译。

这里插句题外话,很多动态语言底层都是C或者C++,例如Python,ruby底层都是C,这两门语言里面也有block的概念,只不过在python里称为lambda。所以他们的实现方式是相似的。

OK,回到正题。首先,我们通过clang -rewrite-objc filename(clang是编译指令) 将含有block的源文件转换为C源代码,OC源代码如下:

int main() {
  //block实现
  void (^test)(void) = ^{
     printf("Hello World\n");
  };

  test();

  return 0;
}

代码很简单,只是打印一个字符串.但是转换后却又10000+的代码,但是和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;
       }
  };

<br />

  static void __main_block_func_0(struct __main_block_impl_0 *__cself){
      printf("Hello World!\n");
  }

<br />

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

<br />

   int main() {
   void (*test)(void) = (void (*)())&__main_block_impl_0((void *)__main_block_func_0,       
    &__main_block_desc_0_DATA);
  ((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)
  ((__block_impl *)test);

  return 0;
}

上面四段代码就是一个简单Block的C版本,下面我们一一讲解下。

  static void __main_block_func_0(struct __main_block_impl_0 *__cself){
    printf("Hello World!\n");
  }

这段代码代表的就是block的实现,其中__main_block_func_0代表的是主函数中第0个函数,其参数是Block本身,类似C++的this指针和OC的self.

下面我们来看看cself的类型:__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, intflags=0){
          impl.isa = &_NSConcreteStackBlock;
          impl.Flags = flags;
          impl.FuncPtr = fp;
          Desc = desc;
   }
  };

可以看到这个类型是个结构体,里面有两个结构体参数,外加一个构造方法.其中变量impl是另一个结构体,我们来看下它的内容:

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

除了耳熟能详的的isa指针外,我们还看到flags,估计是某些标志;reserved,升级预留区域,以及一个函数指针.

再看看Desc的结构:

struct __main_block_desc_0{
unsigned long reserved;
unsigned long Block_size;
}

代表的是版本升级预留区域,和block大小.

下面我们看看其构造函数的调用:

  void (*test)(void) = (void (*)())&__main_block_impl_0((void
    *)__main_block_func_0, &__main_block_desc_0_DATA);  
  }

其实就是将__main_block_impl_0结构体赋值给test变量,顺带初始化其他属性.
之后,调用:

  ((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)
   ((__block_impl *)test);  
  }

这其实就是一个简单的函数指针调用函数,类似(*name)(paramters..)调用.

致此,block的实质就讲的差不多了,漏了一个_NSConcreteStackBlock下次再讲吧.

    原文作者:尘絮缘12138
    原文地址: https://www.jianshu.com/p/b66fb5a69598
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞