iOS开发-关于block块的实现

关于 block 块的实现原理,以及对外部变量的拷贝和引用问题。

block文档 做了详细说明,这里再复习一遍,并以自己的理解方式写出。

block文档 描述了块的Apple ABI实现规范,其实就是编译器对 block 的转化实现,去了解一个 block 声明时,编译器是怎么构建的。

block的结构体

这个ABI的第一个发行版本是在Mac OS X 10.6中找到的,称为10.6.ABI。从2010年3月16日起,第二个版本,必要时将称为ABI.2010.3.16

编译器会考虑runtime运行时的需要各种引用关系来定义blockABI,也就是说,编译器会根据上下文,引入的参数,来定义不同的ABI,(我们可以通过-rewrite-objc来将OC文件编译成cpp实现来证实),一个block的结构体如下:

struct Block_literal_1 {
    void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
    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
};

isa 可以看到block被设计成一个对象,可以相应消息。

flag 字段用于表示编译器生成的block“类型”,后面有说明,其枚举如下:

对于 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) 通常被设置,并且总是被runtime忽略(它是一个转换标记,在转换之后没有被删除)。该位现在与 (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,
}

这里不理解 flag 没事,先接着看。

没有引入变量的block

对于以下 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
};

可以见得,这里的flag 就是 1<<29

block以static常量修饰时,那么它的实现会是:

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

flag 添加了 BLOCK_IS_GLOBAL 表示这是一个全局的。

引入变量的block

变量如果没有__block 修饰,则会自动拷贝一份const值到 block 结构体中,不再是之前的那个变量。这也是我们需要修改 block 外的值时,必须使用__block 前缀的原因。

如果使用了__block 修饰,编译器则会将指针复制,并将其包含到一个结构体(这里就叫A吧)中,然后编译器生成的 block 的结构体就包含这个A

引入常量

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

这段代码输出则是x is 10,因为拷贝的是,编译器会编译为:

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

可以见得 const int x 则复制了x的值,此x非彼x

然后 block 结构体的定义:

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

对于这样的常量拷贝形式,是不需要 Runtime Helper Functions 的.
Runtime Helper Functions 就是用来支持runtime机制的,因为一个对象NSObject会调用object_release来释放,而block赋值的变量指针对象并没有在这个范畴内,所以需要 Runtime Helper Functions ,使得runtime 能够管理其引用关系并及时释放。

引入block

需要copy and dispose helper functions的第一种情况是在导入block本身的情况下。在这种情况下,需要copy_helper函数dispose_helper函数copy_helper函数既传递现有的基于堆栈的指针,也传递指向新堆的指针,它应该回调到runtime中,对block中导入的字段实际执行copy操作。运行时的方法都是在 Runtime Helper Functions(后续说明)中描述的。

看下面这个例子

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

编译器会编译为:

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

1<<25 表示 BLOCK_HAS_COPY_DISPOSE,表示需要辅助函数 helper functions 处理runtime 的引用计数相关(或者更多)。需要辅助函数就意味着是拷贝指针。

引入 attribute((NSObject)) 变量

GCC结构指针上引入了_attribute__((NSObject))来表示“这是一个对象”
这很有用,因为许多低层数据结构都声明为不透明的结构指针,例如CFStringRef、CFArrayRef

例如下面这部分代码,声明一个block foo

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

将会创建helper方法,这个拷贝的helper function将由编译器创建,而且应该使用_Block_object_assign 这个运行时方法,然后在释放时,回到用 _Block_object_dispose ,这也是和 runtime 关联的方法,相当于NSObject 对象的引用以及释放。

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 中,但会包裹一层结构放在 block 中,这个结构就是下面的_block_byref_<name>:

byref 意思为指针引用

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 functions

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

这个结构体这样初始化的:

  1. forwarding 被设置为这个闭包结构的开始
  2. size字段初始化为闭包结构的总大小
  3. 如果不需要辅助函数,则flags字段设置为0,如果需要,则设置为(1<<25)
  4. 辅助函数被初始化(如果存在)。
  5. 变量本身被设置为其初始值
  6. isa字段被设置为NULL
  • 为什么需要设置forwarding指向自己呢?
    因为block创建时都是在栈上,当blockcopy到堆上时,就会存在两份自己,一份位于,一份位于forwarding在这里就会指向的那个自己,而不会指向随时可能释放的的自己。

访问__block的变量(声明范围内)

前面说了 __block 修饰的变量会被编译器转义为_block_byref_<name>,那么当我们访问它时,又有什么不同呢? 看下面这段代码

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;

那么问题又来了,对于需要引用计数的对象呢?对的,是应该就需要helper functions

如下代码:

__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的变量(block内)

我们知道 __block 会直接拷贝指针引用 ,所以 __block 声明的变量都必须实现 helper fuctions 以便于运行时进行拷贝释放指针,利用_Block_object_assign_Block_object_dispose

例如:

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

然后block 初始化为:

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 修饰的变量,应该也具有 helper function ,和 __block 修饰的变量一样。

block 的释放

因为引用了_block变量block可能会执行Block_copy(),所以这些变量的底层存储可能会移动到中。在Objective-C垃圾收集中,只有编译环境中使用的才是垃圾收集,不需要进一步的操作。否则,编译器必须发出一个调用,以便在所有的转义终止作用域时释放任意的存储。调用应该是:

_Block_object_dispose(&_block_byref_foo, BLOCK_FIELD_IS_BYREF);

block 嵌套

block A 中可以包含 block B ,所以按之前所说,block A 结构中可能包含一个成员 _block_literal (为B) ,所有B中包含的常量变量都将会被拷贝到block A 即使没有被使用。这也包括const常量以及block块

Object-C扩展

block中引入一个NSObject对象

对象应该被看作是_attribute__((NSObject))变量;所有的copy_helper、dispose_helper、byref_keepbyref_dispose帮助函数都应该使用_Block_object_assign_Block_object_dispose。不应该生成使用*-retain*-release方法的代码。

block当作为一个对象

编译器在合成属性settergetter时将block视为对象,和对象一样能够进行强引用和弱引用

__weak __block支持

Objective-C(和objective - c++)支持在block变量上的_weak属性。通常情况下,编译器会使用Objective-C运行时helper fucntion调用objc_assign_weakobjc_read_weak。这两种方法都应该用于对所有__weak __block变量的读写:

对于常量 i

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

_weak变量存储在_block_byref_foo结构中,Blockcopydisposehelper函数,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);

对于对象

和常量的区别除了拷贝指针外,在于flag不同,使用 BLOCK_FIELD_IS_OBJECT

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

例如:

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

会被转化为:

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

__weak __block 修饰的则是flag的不同。

对于block

使用 BLOCK_FIELD_IS_BLOCK

_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
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 };

对象block两者初始化都为

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++对象存在于block块中时,flag则会标记为(1<<26),当然同时还有(1<<25)

{
    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_literal_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前缀

__block FOO blockStorageFoo;

那么构建时就需要

FOO_ctor(& _block_byref_blockStorageFoo->blockStorageFoo);

析构:

FOO_dtor(& _block_byref_blockStorageFoo->blockStorageFoo);

这里指的注意的是并没有使用forwarding指针

编译器生成的helper fuctions如下:

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 functions 可以在 /usr/local/include/Block_private.h.查看描述。

helper fuctions 应该对每个block调用

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

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

值得注意的是编译器包裹这两个函数的函数命名是__block_copy_i__block_dispose_

  1. __block_copy_blockcopy到堆上时被调用
  2. __block_dispose_在堆上的block被释放时调用

block何时会触发copy呢?

  1. 调用Blockcopy实例方法
  2. Block作为函数返回值返回时
  3. Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
  4. 在方法名中含有usingBlockCocoa框架方法或Grand Central DispatchAPI中传递Block

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

其含义和使用上面的例子已经用到了
BLOCK_FIELD_IS_OBJECT 表示对象
BLOCK_FIELD_IS_BLOCK 表示一个block对象
BLOCK_FIELD_IS_BYREF 表示指针引用,意味着拷贝指针
BLOCK_FIELD_IS_WEAK 表示__weak属性
BLOCK_BYREF_CALLER 表示使用helper functions实现copy/dispose方法,兼容runtime

附上源代码中的代码摘要:

/* 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);

这篇文章已经了解了block的实现和参数捕获,那么block带参数时,编译器是如何构建的呢?再者,我们如何创建一个参数个数不确定时,如何创建block

带形参的block

main.m 我们通过反编译


#import <stdio.h>

int main () {
    void (^block)(int value, char* str) = ^(int value, char* str) {
        printf("value : %d str : %s",value,str);
    };
    block(2,"char test");
    return 0;
}

clang -rewrite-objc main.m -o test.cpp

得到

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, int value, char *str) {

        printf("value : %d str : %s",value,str);
    }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main () {
    void (*block)(int value, char* str) = ((void (*)(int, char *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    ((void (*)(__block_impl *, int, char *))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 2, "char test");
    return 0;
}

block参数,由定义的func决定,那么等于就是block映射了一个c函数,这个函数的形参和block的形参个数一致。

static void __main_block_func_0(struct __main_block_impl_0 *__cself, int value, char *str) {

        printf("value : %d str : %s",value,str);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值