iOS_Block

2018-09-06  本文已影响0人  自律_自强_通达

下面我们转换block语法。

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

此源代码的block语法最为简单,它省略了返回值类型以及参数列表。该源代码通过clang可转变为以下形式:

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 {
  unsigned long reserved;
  unsigned long Block_size;
} __mian_block_desc_0_DATA = {
  0,
  sizeof(struct __main_block_impl_0)
};

int main () {
  void (*blk) (void) = (void (*) (void)) &__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);
  ((void (*) (struct __block_impl *))((struct __block_impl * )blk)->FuncPtr)((struct __block_impl *) blk);
  return 0;
} 

5行代码增加到了 35行,但是这段代码并不复杂。
下面,我们逐个分解代码:
分解前:

^{printf("block\n");};

分解后,里面也有对应代码。

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

如变换后的源代码所示,通过blocks使用的匿名函数,实际上被作为简单的C语言函数来处理。另外,根据block所属的函数名(此处为main)和该block在该函数出现的顺序值(此处为0)来给经clang变换过的函数命名

该函数的参数__cself相当于C++实例方法中指向实例自身的变量this,或者是OC实例方法指向对象自身的变量self,即参数__cself指向block值的变量。

不过本次这个__main_block_func_0 函数并不使用__cself。我们来看下参数的声明:

struct __main_block_impl_0 * __cself

与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 结构体会变得非常简单。第一个成员变量impl,我们先看下__block_impl结构体的声明:

struct __block_impl {
  void *isa;
  int Flags;// 某些标志
  int Reserved;// 今后版本升级所需的区域
  void *FuncPtr;// 函数指针
};

我们从其名称可以联想到某些标志、今后版本升级所需的区域以及函数指针。第二个成员变量时Desc指针,以下为其 __main_block_desc_0 结构体声明:

struct __main_block_desc_0 {
  unsigned long reserved;// 今后版本升级所需区域
  unsigned long Block_size; // Block的大小
};

这些也如其成员变量所示,其结构为今后版本升级所需区域和Block的大小。
那么,下面我们来看下初始化这些结构体的 __main_block_impl_0 结构体的构造函数。

__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结构体成员的源代码。我们刚刚跳过了_NSConcreteStackBlock 的说明。_NSConcreteStackBlock用于初始化__block_impl结构体的isa成员。在了解它之前,我们先看下该构造函数的调用。

// 此处为了方便阅读,给代码折行了。
void (*blk) (void) =
         (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_0_DATA);
__main_block_impl_0 *blk = &tmp;

这样就容易理解了。该源代码将__main_block_impl_0 结构体类型的自动变量,即栈上生成的__main_block_impl_0结构体实例指针,赋值给__main_block_impl_0指针类型的变量blk。以下这部分代码对应最初的源代码。

void (^blk) (void) = ^{printf("block\n");};

那么,现在我们下结论,block就是带有isa指针和其它成员的结构体。
和OC的对象本质是一样的。如果你想知道OC对象的本质,可以百度以下,这个地方暂时不做讲解。

上一篇 下一篇

猜你喜欢

热点阅读