Block LLVM

官方链接

目录.png

High Level

The ABI of Blocks consist of their layout and the runtime functions required by the compiler.

blockABI (Application Binary Interface应用程序二进制接口) 由 '他们的结构布局' 和 '编译器所需的运行时函数' 组成.

block 布局结构 :

struct Block_literal_1 {
    // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
    void *isa; 
    int flags;
    int reserved;
    void (*invoke)(void *, ...);
    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
};

flags:

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

BLOCK_HAS_STRET(1 << 29) 通常被设置且总是被 runtime 忽略, 它是一个过渡标记, 转换后没有被删除, 现与 BLOCK_HAS_SIGNATURE(1 << 30) 配对, 表示为 (3 << 30):

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, presence of signature
  case (3<<29): ABI.2010.3.16, stret calling, presence of signature
}

block 可能发生在函数中, 其结构创建在栈局部内存中. 也可能作为全局 block 变量或静态局部变量的初始化表达式存在.

基于栈结构的初始化:

  1. static descriptor structure 静态描述结构的声明和初始化:
    • invoke 函数指针会被设置一个以 block结构体作为第一个参数, 其余参数为 block执行复合语句时的参数的 函数.
    • size 作为 block结构体 的大小
    • copy_helperdispose_helper 函数指针会在 block 需要时候设置
  2. A stack (or global) Block literal data structure 栈 block 或全局 block 的声明和初始化:
    • isa 被设置为 _NSConcreteStackBlock, 是由系统提供的一个没有初始化的 block 内存, 如果是静态或者文件级则会被设置为 _NSConcreteGlobalBlock.
    • flags 被设置为 0, 除非 block 引用的变量需要程序级 Block_copy() 和 Block_release() 操作的 helper 函数, 这种情况下 flags 会设置为 (1 << 25).

example:

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

会创建如下代码:

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

// block 执行体
void __block_invoke_1(struct __block_literal_1 *_block) {
     printf("hello world\n");
}

// block 描述符结构体
static struct __block_descriptor_1 { 
    unsigned long int reserved;
    unsigned long int Block_size;
} __block_descriptor_1 = { 
        0, 
        sizeof(struct __block_literal_1), 
        __block_invoke_1 
    };

block 初始化:

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

当 block 作为全局或静态局部变量时, 初始如下:

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

和栈 block 相比, 只是标记 flags 为全局, isa 指向修改, 这是一种优化, 可用于任何没有引入 const 或 __block变量的 block.

Imported Variables

自动存储类变量作为 const 引入
__block 存储变量作为一个指向一个结构体的指针
全局变量简单引用, 不视为引入

Imported const copy variables

没有使用 __block 标记的自动变量 被作为 const 副本.

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

会被编译为:

struct __block_literal_2 { // block
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(struct __block_literal_2 *);
    struct __block_descriptor_2 *descriptor;
    const int x; // const 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) };

and 初始化:

struct __block_literal_2 __block_literal_2 = {
      &_NSConcreteStackBlock,
      (1<<29),
      <uninitialized>,
      __block_invoke_2,
      &__block_descriptor_2,
      x // 赋值 x 的值.
 };

总之,标量,结构体,联合体和函数指针通常作为 const 副本导入,不需要 helper 函数。

Imported const copy of Block reference

引入 block 的 const 副本
需要使用辅助函数 copy_helperdispose_helper 的第一种情况是引入 block(类型)变量(existingBlock), copy_helper 传递 existingBlock 的指针和堆中的副本的指针, block 中引入的字段的 copy 操作应回调到 runtime 中确认.

block 引入 block 的 example:

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_descriptor_4 *descriptor;
    struct __block_literal_3 *const existingBlock;
};

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

// 传入栈和堆中的 block 的指针
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    // 包含 copy 和 dispose
      existingBlock,            // 引入的 block
};

Importing __attribute__((NSObject)) variables

GCC 在结构指针上引入了 __attribute__((NSObject)) 来表示“这是一个对象”, 许多低层数据结构都声明为不透明的结构指针,例如CFStringRef、CFArrayRef 等, 在 C 语言中这些仍然是真正的对象, 这是需要生成 copy_helperdispose_helper 的第二种情况, copy_helper 生成需要调用 _Block_object_assign runtime 方法 dispose_helper 调用 _Block_object_dispose 方法.

example:

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

生成以下 helper 函数:

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

Imported __block marked variables

Layout of __block marked variables

编译器必须将标记为 __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() 时, 某些类型的变量需要使用 helper 函数.
在 C 语言中, 只有 Block类型 或者 __attribute__((NSObject))标记的变量 才需要 helper 函数.
在 Objective-C 中, 对象需要 helper 函数, 而在 C++ 堆栈中, 基于栈的对象需要 helper 函数.

需要使用 helper 函数的形式:

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

_block_byref_foo :

  • The isa field is set to NULL.
  • forwarding 指针设置结构体地址.
  • flags 如果不需要 helper 函数则设置为0. 如果是(1 << 25).
  • size 初始为结构体的总大小.
  • byref_keep/byref_dispose helper 函数指针.
  • marked_variable 变量自身, 设置为初始值.

Access to __block variables from within its lexical scope (__block 词法)

通过 copy_helper 操作将变量移动到堆上, 编译器必须通过结构体的 forwarding 指针重写这类变量的访问.
example:

int __block i = 10;
i = 11;

会被重写为:

struct _block_byref_i {
  void *isa;
  struct _block_byref_i *forwarding; // 指向自身结构体 i 的地址.
  int flags;   //refcount;
  int size;
  int captured_i; // 捕获 i 的值为 10
} i = { NULL, &i, 0, sizeof(struct _block_byref_i), 10 };

i.forwarding->captured_i = 11;

__block 标记的 block 变量, helper 的代码必需通过 runtime 使用 _Block_object_assign_Block_object_dispose 生成用来制作副本.

example:

// 将 voidBlock 用 __block 标记, 会生成一个结构体
__block void (voidBlock)(void) = blockA;
voidBlock = blockB;

转换为:

struct _block_byref_voidBlock {
    void *isa;
    struct _block_byref_voidBlock *forwarding;
    int flags;   //refcount;
    int size;
    // block 需要 helper 函数
    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)
}

and :

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;

Importing __block variables into Blocks (引入__block 标记变量)

block 复合语句体中使用 __block 变量, 必须引入变量并给出 copy_helperdispose_helper 函数, 回调到运行时, 实际 copy 或者 release byref-block 是使用 _Block_object_assign_Block_object_dispose 函数.
example:

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

转换为 :

// __block i 生成的结构体
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;
};

// block 结构体
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;
};

// block 复合语句体
void __block_invoke_5(struct __block_literal_5 *_block) {
    // _block->forwarding为 byref_i 结构体, 取值改值 captured_i
    _block->i_holder->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 = {( // __block 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, // i 为 byref_i 结构体.
};

Importing __attribute__((NSObject)) __block variables

__block 也可以标记 __attribute__((NSObject)) 应该有 byref_keepbyref_dispose helper 函数, 使用 _Block_object_assign_Block_object_dispose.

__block escapes

因为 block 引用 __block 标记的变量可能会对他们执行 Block_copy() 操作, 变量的底层存储可能移动到堆中. 在 Objective-C 中, 垃圾收集只在编译环境中进行, 使用的堆是已收集过的, 不需要进一步的操作. 否则在其作用域的所有转义或终止时编译器必须发出一个调用为 __block变量 释放堆存储:

_Block_object_dispose(&_block_byref_foo, BLOCK_FIELD_IS_BYREF);

Nesting

block 嵌套 block 的情况下, 内部block 引入的任何变量都会被引入到所有封闭范围, 即使没有使用, 这包括 const 和 __block 的方式引入.

Objective C Extensions to Blocks

Importing Objects (引入对象)

objects 应以 __attribute__((NSObject)) 对待.
所有的 copy_helper, dispose_helper, byref_keep, and byref_dispose helper 函数都应该使用 _Block_object_assign and _Block_object_dispose. 使用 -retain-release 不会生成任何其他代码.

Blocks as Objects (作为对象)

编译器在合成属性 settergetter 时将 block 视为对象,在以与对象相同的方式 生成 垃圾收集 strong 和 weak 信息 时将它们描述为对象,并将以与对象相同的方式 发出 强弱写入屏障分配。

__weak __block Support

OC 支持 __weak 属性的 __block 变量, 正常情况下, 编译器使用 runtime 的objc_assign_weak and objc_read_weak 两个 helper 函数用来支持 __weak __block 变量的读写:

// runtime 接口, 用来读取block 内部的 __block / __weak 变量.
objc_read_weak(&block->byref_i->forwarding->i)

__weak变量被存到一个 _block_byref_foo结构体中, block 提供 copy 和 dispose helper 函数, 以便结构体调用:

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

and:

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

block_byref 结构体会区分 __block 变量 是否是 block(也可能是对象) 进行调用:

object:

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

block:

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

完整的例子:

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

the block byref part:

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

block part :

// block
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;
};

// block_invoke
void __block_invoke_5(struct __block_literal_5 *_block) {
   // 通过 runtime 读取 block 捕获的 obj
   [objc_read_weak(&_block->byref_obj->forwarding->captured_obj) 
                                                             somemessage];
}

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

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

// block_descriptor
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 
    };

the compound statement part:

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

struct __block_literal_5 _block_literal = {
     &_NSConcreteStackBlock,
     (1<<25)|(1<<29),
     <uninitialized>,
     __block_invoke_5,
     &__block_descriptor_5,
     &obj, // _block_byref_obj 指针引用 containing "captured_obj"
};

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

C++ Support

一个 block内的 基于C++堆栈的对象, 使用 copy constructor 拷贝为 const 副本.
如果这个对象没有 copy constructor, 就会报错.
为了 block 支持 Block_copy() 操作, 必须为 block 合成 copy 和 destroy helper 函数, 并且 flags 除了标记为(1 << 25)外, 还要使用(1 << 26).

copy helper 通过基于 block堆栈 适当的偏移量调用 copy constructor 创建 const 副本, 调用 destructor 的逻辑与之相似.

block 中引入 C++ 对象的例子:

{
    FOO foo; // C++ obj
    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_literal_10(struct __block_literal_10 *dst, 
                        struct __block_literal_10 *src) {
     // C++ constructor
     FOO_ctor(&dst->foo, &src->foo);
}

void __block_dispose_10(struct __block_literal_10 *src) {
     // C++ distructor
     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 
    };

the code :

{
  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,
   };

   // block 栈中 const 版本.
   comp_ctor(&_block_literal->foo, &foo); 
    
   // block 赋值
   struct __block_literal_10 &block = &_block_literal; 
    
   // invoke block 调用
   block->invoke(block);
      
   // destroy stack version of const block copy 销毁栈中const 副本对象
   comp_dtor(&_block_literal->foo); 
   
   // destroy original version 销毁原始对象
   comp_dtor(&foo); 
}

__block C++对象和其他变量一样开始存储在 block_byref结构中的堆栈上, 这些对象(不是const对象)必须有一个常规的 copy constructor, 编译器为 block_byref 结构体 合成 copy 和 destroy helper 函数, copy helper 函数会生成执行 block_byrefcopy constructor, 并设置(1 << 25)和(1 << 26)位. destroy helper 函数会对在 block_byref 结构体中存储的对象执行 destructor.
例如:

__block FOO blockStorageFoo;

constructor:

FOO_ctor(& _block_byref_blockStorageFoo->blockStorageFoo);

the destructor:

FOO_dtor(& _block_byref_blockStorageFoo->blockStorageFoo);

注意, 没有使用 forwarding.
编译器将需要(为 FOO 类/结构体)生成(如果使用在 block 中)以下的copy/dispose helper:

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

为了支持成员变量和函数访问, 编译器将合成一个指向该指针的 block版本 的 const 指针.

Runtime Helper Functions

runtime helper 方法描述在 /usr/local/include/Block_private.h 中, 总的来说, 如果block 引入了 block 变量, __block变量, __attribute__((NSObject)) 变量, 或者 通过 constructor/destructor 拷贝的 C++对象, 需要 copy/dispose helper 函数. 如果函数生成则设置(1 << 26).

block 的 copydispose 函数:
copy:

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

dispose:

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

apropos:

enum {
    // id, NSObject, __attribute__((NSObject)), block, ...
    BLOCK_FIELD_IS_OBJECT   =  3,  
    BLOCK_FIELD_IS_BLOCK    =  7,  // a block variable
    
    // the on stack structure holding the __block variable
    BLOCK_FIELD_IS_BYREF    =  8,  // 栈结构体持有 __block 变量
    BLOCK_FIELD_IS_WEAK     = 16,  // declared __weak

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

block_byref 结构同样需要 对 block变量_attribute__((NSObject))变量c++ 使用constructor/destructor const复制对象 使用 copy/dispose helper, 同样设置(1<<26)位, 并以相同的方式生成函数.

ObjC 下允许__weak 属性修饰 __block, 这导致在 block_byref 中调用 copy/dispose helper 时需要的标记位组合 BLOCK_FIELD_<apropos>.

helper 函数的原型:

/* 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.
*  某些字段类型在复制到堆时需要运行时帮助. 下面的函数用于复制类型的字段:
   blocks、指向byref结构的指针和对象(包括_attribute__((NSObject))指针). 
   BLOCK_FIELD_IS_WEAK 正交于其他互斥的选项. 
   BLOCK_FIELD_IS_BYREF 仅仅只存在 Block copy helper 函数中.
*/
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:
*  类似地, 编译器生成 dispose helper 需要对 byref 结构体的每个字段进行回调.
   (目前实现只将一个字段打包到byref结构中,但原则上可以有更多). 
   相同的, copy helper 中使用的 flags标志应该被该函数生成的所有调用使用. 
*/
void _Block_object_dispose(const void *object, const int flags);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,468评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,620评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,427评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,160评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,197评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,334评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,775评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,444评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,628评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,459评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,508评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,210评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,767评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,850评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,076评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,627评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,196评论 2 341

推荐阅读更多精彩内容