iOS_Block

作者: 自律_自强_通达 | 来源:发表于2018-09-06 16:01 被阅读0次
  • 定义:带有自动变量(局部变量)匿名函数

    • 匿名函数:顾名思义,所谓匿名函数就是不带有名称的函数。
  • block语法:

    • 完整形式的block定义与一般C函数定义相比,仅有两点不同:
      • 没有函数名:因为它是匿名函数,所以没有函数名。
      • 带有“^”:“ ^ ” (插入记号,caret)记号,在大量使用block的时候方便查找。
    • 语法格式
      ^ 返回值类型 参数列表 表达式
      返回值类型和参数列表可以省略。
  • block类型变量

    • 声明block类型变量
      int (^ blk) (int)
    • block 变量和C变量完全相同,可以作为以下用途使用:
      • 自动变量
      • 函数参数
      • 静态变量
      • 静态全局变量
      • 全局变量
    • 那么下面我们讲block赋值为block类型变量:
      int (^ blk) (int) = ^ (int count) {return count + 1;};
      int (^ blk1) (int) = blk;
      int (^ blk2) (int);
      blk2 = blk1;
  • “带有自动变量”究竟是什么呢?
    这句话在blocks中表现为“截获自动变量”,“截获”的含义是,当block执行时,自动变量的瞬间值,会被保存下来,在block中使用。即使后面代码中,该自动变量被重新赋值也不不会影响block中的值。

  • __block说明符
    实际上被截获的自动变量值,在block中是不能被改写的,如果要改写,这时就需要在前面加上“__block”

  • block的实质
    block是“带有自动变量的匿名函数”,那么block究竟是什么呢?答:OC对象。

    • 分析: block实际上是作为极普通的C语言代码来处理的,通过支持block的编译器,block部分代码会被转换成一般C语言编译器能够处理的源代码,并作为C语言源代码被编译。实际操作是 clang(LLVM编译器)通过“-rewrite-objc” 选项将block部分的代码转换成C++代码,这里只是使用了C++的struct结构,本质还是C语言源代码。
      clang -rewrite-objc 源代码文件名

下面我们转换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对象的本质,可以百度以下,这个地方暂时不做讲解。

相关文章

  • iOS_Block

    定义:带有自动变量(局部变量)的匿名函数。匿名函数:顾名思义,所谓匿名函数就是不带有名称的函数。 block语法:...

  • iOS_Block

    block的本质其实就是OC对象, Block是oc的一段代码块,在需要的时候调用。 Block变量的声明格式为:...

  • iOS_block问题

    前言: 最近遇到了一个block的循环引用的问题,才发现我对block还一知半解,为此,对自己的理解做了一下正整理...

网友评论

      本文标题:iOS_Block

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