块实现规范

历史

  • 2008/7/14 - 创建。

  • 2008/8/21 - 修订,C++。

  • 2008/9/24 - 添加 NULL isa 字段到 __block 存储。

  • 2008/10/1 - 修订块布局以使用 static 描述符结构。

  • 2008/10/6 - 修订块布局以使用无符号长整型标志。

  • 2008/10/28 - 指定使用 _Block_object_assign_Block_object_dispose 用于帮助函数中的所有“对象”类型。

  • 2008/10/30 - 修订新布局以将调用函数置于同一位置。

  • 2008/10/30 - 添加 __weak 支持。

  • 2010/3/16 - 修订用于 stret 返回、签名字段。

  • 2010/4/6 - 改进了措辞。

  • 2013/1/6 - 改进了措辞并转换为 rst。

本文档描述了 Apple ABI 块实现规范。

此 ABI 的第一个发布版本在 Mac OS X 10.6 中找到,将被称为 10.6.ABI。截至 2010/3/16,以下内容描述了与运行时和编译器之间的 ABI 契约,并且必要时,将被称为 ABI.2010.3.16。

由于 Apple ABI 引用了系统其他元素中的符号,因此在 SnowLeopard 之前系统上使用此 ABI 的任何尝试都是未定义的。

高级

Blocks 的 ABI 由其布局和编译器所需的运行时函数组成。类型为 R (^)(P...)Block 包含以下形式的结构

struct Block_literal_1 {
    void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
    int flags;
    int reserved;
    R (*invoke)(struct Block_literal_1 *, P...);
    struct Block_descriptor_1 {
        unsigned long int reserved;     // NULL
        unsigned long int size;         // sizeof(struct Block_literal_1)
        // optional helper functions
        void (*copy_helper)(void *dst, void *src);     // IFF (1<<25)
        void (*dispose_helper)(void *src);             // IFF (1<<25)
        // required ABI.2010.3.16
        const char *signature;                         // IFF (1<<30)
    } *descriptor;
    // imported variables
};

以下标志位用于可能的 ABI.2010.3.16

enum {
    // Set to true on blocks that have captures (and thus are not true
    // global blocks) but are known not to escape for various other
    // reasons. For backward compatibility with old runtimes, whenever
    // BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. Copying a
    // non-escaping block returns the original block and releasing such a
    // block is a no-op, which is exactly how global blocks are handled.
    BLOCK_IS_NOESCAPE      =  (1 << 23),

    BLOCK_HAS_COPY_DISPOSE =  (1 << 25),
    BLOCK_HAS_CTOR =          (1 << 26), // helpers have C++ code
    BLOCK_IS_GLOBAL =         (1 << 28),
    BLOCK_HAS_STRET =         (1 << 29), // IFF BLOCK_HAS_SIGNATURE
    BLOCK_HAS_SIGNATURE =     (1 << 30),
};

在 10.6.ABI 中,(1<<29) 通常被设置,并且始终被运行时忽略 - 它一直是一个过渡标记,在过渡后没有被删除。现在此位与 (1<<30) 配对,表示为对 (3<<29),用于以下有效位设置组合及其含义

switch (flags & (3<<29)) {
  case (0<<29):      10.6.ABI, no signature field available
  case (1<<29):      10.6.ABI, no signature field available
  case (2<<29): ABI.2010.3.16, regular calling convention, presence of signature field
  case (3<<29): ABI.2010.3.16, stret calling convention, presence of signature field,
}

签名字段并不总是填充。

以下讨论以 10.6.ABI 为准。

Block 文本可能出现在结构在堆栈局部内存中创建的函数中。它们也可能作为全局或 static 局部变量的 Block 变量的初始化表达式出现。

Block 文本表达式被求值时,基于堆栈的结构将按以下方式初始化

  1. 一个 static 描述符结构被声明并按以下方式初始化

a. invoke 函数指针被设置为一个函数,该函数将 Block 结构作为其第一个参数,以及 Block 的其余参数(如果有),并执行 Block 复合语句。

b. size 字段被设置为以下 Block 文本结构的大小。

c. copy_helperdispose_helper 函数指针如果被 Block 文本需要,将被设置为各自的帮助函数。

  1. 一个堆栈(或全局) Block 文本数据结构被创建并按以下方式初始化

    a. isa 字段被设置为外部 _NSConcreteStackBlock 的地址,它是在 libSystem 中提供的未初始化内存块,或者如果这是静态或文件级 Block 文本,则为 _NSConcreteGlobalBlock

    b. flags 字段被设置为零,除非有变量被导入到 Block 中,这些变量需要帮助函数才能执行程序级 Block_copy()Block_release() 操作,在这种情况下,(1<<25) 标志位将被设置。

例如,Block 文本表达式

^ { printf("hello world\n"); }

会导致在 32 位系统上创建以下内容

struct __block_literal_1 {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(struct __block_literal_1 *);
    struct __block_descriptor_1 *descriptor;
};

void __block_invoke_1(struct __block_literal_1 *_block) {
    printf("hello world\n");
}

static struct __block_descriptor_1 {
    unsigned long int reserved;
    unsigned long int Block_size;
} __block_descriptor_1 = { 0, sizeof(struct __block_literal_1) };

以及 Block 文本本身出现的地方

struct __block_literal_1 _block_literal = {
     &_NSConcreteStackBlock,
     (1<<29), <uninitialized>,
     __block_invoke_1,
     &__block_descriptor_1
};

一个 Block 导入其他 Block 引用、其他变量的 const 复制以及标记为 __block 的变量。在 Objective-C 中,变量还可以是对象。

Block 文本表达式用作全局或 static 局部变量的初始值时,它将按以下方式初始化

struct __block_literal_1 __block_literal_1 = {
      &_NSConcreteGlobalBlock,
      (1<<28)|(1<<29), <uninitialized>,
      __block_invoke_1,
      &__block_descriptor_1
};

也就是说,一个不同的地址作为第一个值提供,并且在 flags 字段中设置了一个特定的 (1<<28) 位,否则它与基于堆栈的 Block 文本相同。这是一种优化,可以用于任何不导入 const__block 存储变量的 Block 文本。

导入的变量

auto 存储类变量被导入为 const 复制。 __block 存储类变量被导入为指向封闭数据结构的指针。全局变量只是被引用,而不是被视为导入的。

导入的 const 复制变量

未用 __block 标记的自动存储变量被导入为 const 复制。

最简单的例子是导入类型为 int 的变量

int x = 10;
void (^vv)(void) = ^{ printf("x is %d\n", x); }
x = 11;
vv();

它将被编译为

struct __block_literal_2 {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(struct __block_literal_2 *);
    struct __block_descriptor_2 *descriptor;
    const int x;
};

void __block_invoke_2(struct __block_literal_2 *_block) {
    printf("x is %d\n", _block->x);
}

static struct __block_descriptor_2 {
    unsigned long int reserved;
    unsigned long int Block_size;
} __block_descriptor_2 = { 0, sizeof(struct __block_literal_2) };

以及

struct __block_literal_2 __block_literal_2 = {
      &_NSConcreteStackBlock,
      (1<<29), <uninitialized>,
      __block_invoke_2,
      &__block_descriptor_2,
      x
 };

总之,标量、结构、联合和函数指针通常被导入为 const 复制,不需要帮助函数。

导入的 const 复制的 Block 引用

第一个需要复制和释放帮助函数的案例是,当 Block 本身被导入时。在这种情况下,需要 copy_helper 函数和 dispose_helper 函数。 copy_helper 函数同时传入现有的基于堆栈的指针和指向新的堆版本的指针,并且应该回调到运行时以实际执行对 Block 中导入的字段的复制操作。运行时函数都在 运行时帮助函数 中描述。

一个简单的例子

void (^existingBlock)(void) = ...;
void (^vv)(void) = ^{ existingBlock(); }
vv();

struct __block_literal_3 {
   ...; // existing block
};

struct __block_literal_4 {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(struct __block_literal_4 *);
    struct __block_literal_3 *const existingBlock;
};

void __block_invoke_4(struct __block_literal_2 *_block) {
   __block->existingBlock->invoke(__block->existingBlock);
}

void __block_copy_4(struct __block_literal_4 *dst, struct __block_literal_4 *src) {
     //_Block_copy_assign(&dst->existingBlock, src->existingBlock, 0);
     _Block_object_assign(&dst->existingBlock, src->existingBlock, BLOCK_FIELD_IS_BLOCK);
}

void __block_dispose_4(struct __block_literal_4 *src) {
     // was _Block_destroy
     _Block_object_dispose(src->existingBlock, BLOCK_FIELD_IS_BLOCK);
}

static struct __block_descriptor_4 {
    unsigned long int reserved;
    unsigned long int Block_size;
    void (*copy_helper)(struct __block_literal_4 *dst, struct __block_literal_4 *src);
    void (*dispose_helper)(struct __block_literal_4 *);
} __block_descriptor_4 = {
    0,
    sizeof(struct __block_literal_4),
    __block_copy_4,
    __block_dispose_4,
};

以及使用该 Block 的地方

struct __block_literal_4 _block_literal = {
      &_NSConcreteStackBlock,
      (1<<25)|(1<<29), <uninitialized>
      __block_invoke_4,
      & __block_descriptor_4
      existingBlock,
};

导入 __attribute__((NSObject)) 变量

GCC 在结构指针上引入了 __attribute__((NSObject)),表示“这是个对象”。这很有用,因为许多底层数据结构被声明为不透明结构指针,例如 CFStringRefCFArrayRef 等等。但是,从 C 使用时,它们仍然是真正的对象,这是需要生成复制和释放帮助函数的第二个案例。编译器生成的复制帮助函数应该使用 _Block_object_assign 运行时帮助函数,而在释放帮助函数中,应该调用 _Block_object_dispose 运行时帮助函数。

例如,以下代码中的 Block foo

struct Opaque *__attribute__((NSObject)) objectPointer = ...;
...
void (^foo)(void) = ^{  CFPrint(objectPointer); };

将生成以下帮助函数

void __block_copy_foo(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
     _Block_object_assign(&dst->objectPointer, src-> objectPointer, BLOCK_FIELD_IS_OBJECT);
}

void __block_dispose_foo(struct __block_literal_5 *src) {
     _Block_object_dispose(src->objectPointer, BLOCK_FIELD_IS_OBJECT);
}

导入的 __block 标记的变量

标记为 __block 的变量布局

编译器必须将标记为 __block 的变量嵌入到以下形式的专用结构中

struct _block_byref_foo {
    void *isa;
    struct Block_byref *forwarding;
    int flags;   //refcount;
    int size;
    typeof(marked_variable) marked_variable;
};

当对引用 Block 执行 Block_copy()Block_release() 时,某些类型的变量需要辅助函数。在“C”级别,只有类型为 Block 或标记为 __attribute__((NSObject)) 的变量需要辅助函数。在 Objective-C 中,对象需要辅助函数,在 C++ 中,基于堆栈的对象需要辅助函数。需要辅助函数的变量使用以下形式

struct _block_byref_foo {
    void *isa;
    struct _block_byref_foo *forwarding;
    int flags;   //refcount;
    int size;
    // helper functions called via Block_copy() and Block_release()
    void (*byref_keep)(void  *dst, void *src);
    void (*byref_dispose)(void *);
    typeof(marked_variable) marked_variable;
};

该结构被初始化,以便

a. 将 forwarding 指针设置为其封闭结构的开头。

b. 将 size 字段初始化为封闭结构的总大小。

c. 将 flags 字段设置为 0(如果不需要辅助函数)或 (1<<25)(如果需要)。

  1. 初始化辅助函数(如果存在)。

  2. 变量本身被设置为其初始值。

  3. isa 字段设置为 NULL

从其词法范围内访问 __block 变量

为了在 copy_helper 操作中将变量“移动”到堆中,编译器必须重写对该变量的访问,使其通过结构的 forwarding 指针进行间接访问。例如

int __block i = 10;
i = 11;

将被重写为

struct _block_byref_i {
  void *isa;
  struct _block_byref_i *forwarding;
  int flags;   //refcount;
  int size;
  int captured_i;
} i = { NULL, &i, 0, sizeof(struct _block_byref_i), 10 };

i.forwarding->captured_i = 11;

如果将 Block 引用变量标记为 __block,则生成的辅助代码必须使用运行时提供的 _Block_object_assign_Block_object_dispose 例程来进行复制。例如

__block void (voidBlock)(void) = blockA;
voidBlock = blockB;

将转换为

struct _block_byref_voidBlock {
    void *isa;
    struct _block_byref_voidBlock *forwarding;
    int flags;   //refcount;
    int size;
    void (*byref_keep)(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src);
    void (*byref_dispose)(struct _block_byref_voidBlock *);
    void (^captured_voidBlock)(void);
};

void _block_byref_keep_helper(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
    //_Block_copy_assign(&dst->captured_voidBlock, src->captured_voidBlock, 0);
    _Block_object_assign(&dst->captured_voidBlock, src->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
}

void _block_byref_dispose_helper(struct _block_byref_voidBlock *param) {
    //_Block_destroy(param->captured_voidBlock, 0);
    _Block_object_dispose(param->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER)}

以及

struct _block_byref_voidBlock voidBlock = {( .forwarding=&voidBlock, .flags=(1<<25), .size=sizeof(struct _block_byref_voidBlock *),
    .byref_keep=_block_byref_keep_helper, .byref_dispose=_block_byref_dispose_helper,
    .captured_voidBlock=blockA )};

voidBlock.forwarding->captured_voidBlock = blockB;

__block 变量导入 Blocks

在复合语句体中使用 __block 变量的 Block 必须导入该变量并发出 copy_helperdispose_helper 辅助函数,这些函数反过来调用运行时来使用 _Block_object_assign_Block_object_dispose 函数实际复制或释放 byref 数据块。

例如

int __block i = 2;
functioncall(^{ i = 10; });

将转换为

struct _block_byref_i {
    void *isa;  // set to NULL
    struct _block_byref_voidBlock *forwarding;
    int flags;   //refcount;
    int size;
    void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
    void (*byref_dispose)(struct _block_byref_i *);
    int captured_i;
};


struct __block_literal_5 {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(struct __block_literal_5 *);
    struct __block_descriptor_5 *descriptor;
    struct _block_byref_i *i_holder;
};

void __block_invoke_5(struct __block_literal_5 *_block) {
   _block->forwarding->captured_i = 10;
}

void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
     //_Block_byref_assign_copy(&dst->captured_i, src->captured_i);
     _Block_object_assign(&dst->captured_i, src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
}

void __block_dispose_5(struct __block_literal_5 *src) {
     //_Block_byref_release(src->captured_i);
     _Block_object_dispose(src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
}

static struct __block_descriptor_5 {
    unsigned long int reserved;
    unsigned long int Block_size;
    void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
    void (*dispose_helper)(struct __block_literal_5 *);
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5) __block_copy_5, __block_dispose_5 };

以及

struct _block_byref_i i = {( .isa=NULL, .forwarding=&i, .flags=0, .size=sizeof(struct _block_byref_i), .captured_i=2 )};
struct __block_literal_5 _block_literal = {
      &_NSConcreteStackBlock,
      (1<<25)|(1<<29), <uninitialized>,
      __block_invoke_5,
      &__block_descriptor_5,
      &i,
};

导入 __attribute__((NSObject)) __block 变量

标记为 __attribute__((NSObject))__block 变量应具有 byref_keepbyref_dispose 辅助函数,这些函数使用 _Block_object_assign_Block_object_dispose

__block 转义

因为引用 __block 变量的 Blocks 可能会执行 Block_copy(),所以变量的基础存储可能会移动到堆中。在仅进行 Objective-C 垃圾回收的编译环境中,使用的堆是垃圾回收的堆,不需要采取进一步的操作。否则,编译器必须在所有转义或终止其范围时发出一个调用来释放 __block 变量的任何堆存储。调用应为

_Block_object_dispose(&_block_byref_foo, BLOCK_FIELD_IS_BYREF);

嵌套

Blocks 可能包含 Block 文字表达式。内部块中使用的任何变量都将导入到所有封闭的 Block 范围内,即使这些变量未被使用。这包括 const 导入以及 __block 变量。

Objective C 对 Blocks 的扩展

导入对象

应将对象视为 __attribute__((NSObject)) 变量;所有 copy_helperdispose_helperbyref_keepbyref_dispose 辅助函数应使用 _Block_object_assign_Block_object_dispose。不应该生成使用 *-retain*-release 方法的代码。

Blocks 作为对象

编译器在合成属性设置器和获取器时会将 Blocks 视为对象,在生成垃圾回收强弱布局信息时会将它们描述为对象,以与对象相同的方式,并且会以与对象相同的方式发出强弱写屏障分配。

__weak __block 支持

Objective-C(和 Objective-C++)支持 __weak 属性在 __block 变量上。在正常情况下,编译器使用 Objective-C 运行时辅助支持函数 objc_assign_weakobjc_read_weak。这两个函数都应该继续用于所有 __weak __block 变量的读写操作

objc_read_weak(&block->byref_i->forwarding->i)

__weak 变量存储在 _block_byref_foo 结构中,并且 Block 为该结构具有复制和释放辅助函数,这些函数调用

_Block_object_assign(&dest->_block_byref_i, src-> _block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);

以及

_Block_object_dispose(src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);

反过来,block_byref 复制支持辅助函数会区分 __block 变量是否为 Block,并且应调用

_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_OBJECT | BLOCK_BYREF_CALLER);

用于声明为对象的内容,或

_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);

用于声明为 Block 的内容。

以下是一个完整的示例

__block __weak id obj = <initialization expression>;
functioncall(^{ [obj somemessage]; });

将转换为

struct _block_byref_obj {
    void *isa;  // uninitialized
    struct _block_byref_obj *forwarding;
    int flags;   //refcount;
    int size;
    void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
    void (*byref_dispose)(struct _block_byref_i *);
    id captured_obj;
};

void _block_byref_obj_keep(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
    //_Block_copy_assign(&dst->captured_obj, src->captured_obj, 0);
    _Block_object_assign(&dst->captured_obj, src->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
}

void _block_byref_obj_dispose(struct _block_byref_voidBlock *param) {
    //_Block_destroy(param->captured_obj, 0);
    _Block_object_dispose(param->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
};

用于块 byref 部分,以及

struct __block_literal_5 {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(struct __block_literal_5 *);
    struct __block_descriptor_5 *descriptor;
    struct _block_byref_obj *byref_obj;
};

void __block_invoke_5(struct __block_literal_5 *_block) {
   [objc_read_weak(&_block->byref_obj->forwarding->captured_obj) somemessage];
}

void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
     //_Block_byref_assign_copy(&dst->byref_obj, src->byref_obj);
     _Block_object_assign(&dst->byref_obj, src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
}

void __block_dispose_5(struct __block_literal_5 *src) {
     //_Block_byref_release(src->byref_obj);
     _Block_object_dispose(src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
}

static struct __block_descriptor_5 {
    unsigned long int reserved;
    unsigned long int Block_size;
    void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
    void (*dispose_helper)(struct __block_literal_5 *);
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5), __block_copy_5, __block_dispose_5 };

以及在复合语句中

truct _block_byref_obj obj = {( .forwarding=&obj, .flags=(1<<25), .size=sizeof(struct _block_byref_obj),
                 .byref_keep=_block_byref_obj_keep, .byref_dispose=_block_byref_obj_dispose,
                 .captured_obj = <initialization expression> )};

truct __block_literal_5 _block_literal = {
     &_NSConcreteStackBlock,
     (1<<25)|(1<<29), <uninitialized>,
     __block_invoke_5,
     &__block_descriptor_5,
     &obj,        // a reference to the on-stack structure containing "captured_obj"
};


functioncall(_block_literal->invoke(&_block_literal));

C++ 支持

在块内,基于堆栈的 C++ 对象使用复制构造函数复制到 const 副本中。如果基于堆栈的 C++ 对象在块内使用但没有复制构造函数,则会发生错误。此外,块必须合成复制和销毁辅助例程才能支持 Block_copy() 操作,并且除了 (1<<25) 位之外,标志还用 (1<<26) 位标记。复制辅助例程应使用提供的基于堆栈的块源和基于堆的目的地中变量的适当偏移量调用构造函数,以便对所有 const 构造的副本进行构造,并且类似地应在销毁例程中调用析构函数。

例如,假设存在一个带有复制构造函数的 C++ 类 FOO。在代码块内,声明了 FOO 对象的堆栈版本,并在 Block 文字表达式中使用

{
    FOO foo;
    void (^block)(void) = ^{ printf("%d\n", foo.value()); };
}

编译器将合成

struct __block_literal_10 {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(struct __block_literal_10 *);
    struct __block_descriptor_10 *descriptor;
    const FOO foo;
};

void __block_invoke_10(struct __block_literal_10 *_block) {
   printf("%d\n", _block->foo.value());
}

void __block_copy_10(struct __block_literal_10 *dst, struct __block_literal_10 *src) {
     FOO_ctor(&dst->foo, &src->foo);
}

void __block_dispose_10(struct __block_literal_10 *src) {
     FOO_dtor(&src->foo);
}

static struct __block_descriptor_10 {
    unsigned long int reserved;
    unsigned long int Block_size;
    void (*copy_helper)(struct __block_literal_10 *dst, struct __block_literal_10 *src);
    void (*dispose_helper)(struct __block_literal_10 *);
} __block_descriptor_10 = { 0, sizeof(struct __block_literal_10), __block_copy_10, __block_dispose_10 };

代码将为

{
  FOO foo;
  comp_ctor(&foo); // default constructor
  struct __block_literal_10 _block_literal = {
    &_NSConcreteStackBlock,
    (1<<25)|(1<<26)|(1<<29), <uninitialized>,
    __block_invoke_10,
    &__block_descriptor_10,
   };
   comp_ctor(&_block_literal->foo, &foo);  // const copy into stack version
   struct __block_literal_10 &block = &_block_literal;  // assign literal to block variable
   block->invoke(block);    // invoke block
   comp_dtor(&_block_literal->foo); // destroy stack version of const block copy
   comp_dtor(&foo); // destroy original version
}

存储在 __block 存储中的 C++ 对象最初存储在堆栈上的 block_byref 数据结构中,就像其他变量一样。此类对象(如果不是 const 对象)必须支持常规复制构造函数。block_byref 数据结构将具有由编译器合成的复制和销毁辅助例程。复制辅助例程将创建代码来执行基于初始堆栈 block_byref 数据结构的复制构造函数,并将设置 (1<<26) 位,以及 (1<<25) 位。销毁辅助例程将创建代码来对提供的 block_byref 堆数据结构中存储的对象执行析构函数。例如,

__block FOO blockStorageFoo;

需要嵌入的 blockStorageFoo 对象的正常构造函数

FOO_ctor(& _block_byref_blockStorageFoo->blockStorageFoo);

以及在范围终止时执行析构函数

FOO_dtor(& _block_byref_blockStorageFoo->blockStorageFoo);

请注意,不会使用转发间接访问。

编译器需要生成(如果从块文字中使用)以下复制/释放辅助函数

void _block_byref_obj_keep(struct _block_byref_blockStorageFoo *dst, struct _block_byref_blockStorageFoo *src) {
     FOO_ctor(&dst->blockStorageFoo, &src->blockStorageFoo);
}

void _block_byref_obj_dispose(struct _block_byref_blockStorageFoo *src) {
     FOO_dtor(&src->blockStorageFoo);
}

用于类/结构 FOO 的适当命名的构造函数和析构函数。

为了支持成员变量和函数访问,编译器将合成指向块版本的 this 指针的 const 指针。

运行时辅助函数

运行时辅助函数在 /usr/local/include/Block_private.h 中描述。总结一下它们的用途,一个 Block 如果它导入任何块变量,__block 存储变量,__attribute__((NSObject)) 变量,或 C++ const 复制对象带有构造函数/析构函数,则需要复制/释放辅助函数。将设置 (1<<26) 位并生成函数。

块复制辅助函数应该为上述类型中的每个变量调用

_Block_object_assign(&dst->target, src->target, BLOCK_FIELD_<apropos>);

在复制辅助函数中,以及

_Block_object_dispose(->target, BLOCK_FIELD_<apropos>);

在释放辅助函数中,其中 <apropos>

enum {
    BLOCK_FIELD_IS_OBJECT   =  3,  // id, NSObject, __attribute__((NSObject)), block, ...
    BLOCK_FIELD_IS_BLOCK    =  7,  // a block variable
    BLOCK_FIELD_IS_BYREF    =  8,  // the on stack structure holding the __block variable

    BLOCK_FIELD_IS_WEAK     = 16,  // declared __weak

    BLOCK_BYREF_CALLER      = 128, // called from byref copy/dispose helpers
};

当然还有 const 复制 C++ 对象的构造函数/析构函数。

类似地,block_byref 数据结构也需要复制/释放辅助函数来处理块变量,__attribute__((NSObject)) 变量,或 C++ const 复制对象带有构造函数/析构函数,并且同样 (1<<26) 位被设置,函数以相同的方式生成。

在 ObjC 中,我们允许 __weak 作为 __block 变量的属性,这会导致添加 BLOCK_FIELD_IS_WEAK 或在 BLOCK_FIELD_IS_BYREF 标志上,在 Block 复制辅助函数中复制 block_byref 结构时,以及在 block_byref 复制/释放辅助函数调用中添加在 BLOCK_FIELD_<apropos> 字段上。

辅助函数的原型和摘要如下:

/* Certain field types require runtime assistance when being copied to the
   heap.  The following function is used to copy fields of types: blocks,
   pointers to byref structures, and objects (including
   __attribute__((NSObject)) pointers.  BLOCK_FIELD_IS_WEAK is orthogonal to
   the other choices which are mutually exclusive.  Only in a Block copy
   helper will one see BLOCK_FIELD_IS_BYREF.
*/
void _Block_object_assign(void *destAddr, const void *object, const int flags);

/* Similarly a compiler generated dispose helper needs to call back for each
   field of the byref data structure.  (Currently the implementation only
   packs one field into the byref structure but in principle there could be
   more).  The same flags used in the copy helper should be used for each
   call generated to this function:
*/
void _Block_object_dispose(const void *object, const int flags);