《iOS开发进阶》第21章:block对象模型

 

这一章主要介绍block在编译器中的具体实现方式。

1、block的内部数据结构定义

block的数据结构定义如下:

struct Block_layout

{

void *isa;

int flags;

int reserved;

void (*invoke)(void *,...);

struct Block_descriptor *descriptor;

Imported variables;

}

 

struct Block_descriptor

{

unsigned long int reserved;

unsigned long int size;

void (*copy)(void *dst,void *src);

void (*dispose)(void *);

}

 

一个block实际由6部分构成:

  1. isa指针,所有对象都有该指针,用于实现对象相关的功能。
  2. flags,用于按bit位表示一些block的附加信息。
  3. reserved,保留变量
  4. invoke,函数指针,指向具体的block实现的函数调用地址
  5. descriptor,表示该block的附加描述信息,主要是size,以及copy和dispose函数的指针。
  6. variables,capture过来的变量,block能够访问它外部的局部变量,就是因为将这些变量复制到了结构体中。

在Objective-C语言中,一共有3种类型的block:

_NSConcreteGlobalBlock,全局的静态block,不会访问任何外部局部变量。

_NSConcreteStackBlock,保存在栈上的block,当函数返回时会被销毁。

_NSConcreteMallocBlock,保存在堆上的block,当引用计数为0时会被销毁。

 

2、用clang分析block实现

命令是:clang -rewrite-objc block.c

NSConcreteMallocBlock类型的block通常不会再源码中直接出现,只有当一个block被调用copy方法的时候,系统才会将这个block复制到堆中。

 

变量的复制

对于block外的变量引用,block默认是将其复制到其数据结构中来实现访问的。如果这个对象是一个引用类型,则block会将其引用计数加1。

对于用_block修饰的外部变量引用,block是复制引用地址来实现访问的。

 

3、注意事项

避免循环引用,由于block会复制外部的变量,所以如果不注意,会比较容易造成循环引用。对于这个问题,需要将引用的另一方变成weak,从而避免循环引用。

 

在ARC开启的情况下,将只会有NSConcreteGlobalBlock和NSConcreteMallocBlock类型的block。

 

本文来自Awnlab.com麦芒实验室,转载请注明出处,谢谢合作。