Contents

当捕获的对象拥有 __block 修饰符之后 Block 结构体中又增加一个成员,准确的说是本来被捕获的变量被继续包装了一层,用一个新的结构体来描述,这个结构体就是 Block_byref

struct Block_byref {
    void *isa;
    struct Block_byref *forwarding;
    volatile int32_t flags; // contains ref count
    uint32_t size;
};

struct Block_byref_2 {
    // requires BLOCK_BYREF_HAS_COPY_DISPOSE
    BlockByrefKeepFunction byref_keep;
    BlockByrefDestroyFunction byref_destroy;
};

struct Block_byref_3 {
    // requires BLOCK_BYREF_LAYOUT_EXTENDED
    const char *lay
    
    out;
};

这里有一个例子

struct __Block_byref_obj_0 {
  void *__isa;
  __Block_byref_obj_0 *__forwarding;
  int __flags;
  int __size;
  void (*__Block_byref_id_object_copy)(void*, void*);
  void (*__Block_byref_id_object_dispose)(void*);
  NSObject *obj; // 这个就是被 __block 修饰的变量本身
};

copy 和 dispose 之上就是 byref 的基本版本,__block 修饰基本类型时就采用基本版本的 byref 来实现;如果涉及到对象,那么就需要管理对象的生命周期,引入 copy 和 dispose 方法。

descriptor 和 byref 拥有类似的设计,即分为基本实现和配备 copy/releaes 方法的处理对象的实现。在前文描述了使用指针操作取到 descriptor_2 的情况,byref 实现中也有类似的地方。但是写法却换了。
下面这段代码将栈上的 block 持有的 __block 对象复制到堆上去。

static struct Block_byref *_Block_byref_copy(const void *arg) {
    struct Block_byref *src = (struct Block_byref *)arg;

    if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
        // src points to stack
        struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
        copy->isa = NULL;
        // byref value 4 is logical refcount of 2: one for caller, one for stack
        copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
        copy->forwarding = copy; // patch heap copy to point to itself
        src->forwarding = copy;  // patch stack to point to heap copy
        copy->size = src->size;

        if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
            // Trust copy helper to copy everything of interest
            // If more than one field shows up in a byref block this is wrong XXX
            // ----------- 在这里,使用以下语法获取 ------------------
            struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
            struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
            copy2->byref_keep = src2->byref_keep;
            copy2->byref_destroy = src2->byref_destroy;

            if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
                struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
                struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
                copy3->layout = src3->layout;
            }

            (*src2->byref_keep)(copy, src);
        }
        else {
            // Bitwise copy.
            // This copy includes Block_byref_3, if any.
            memmove(copy+1, src+1, src->size - sizeof(*src));
        }
    }
    // already copied to heap
    else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
        latching_incr_int(&src->forwarding->flags);
    }
    
    return src->forwarding;
}


/// ???????? 
desc_2 = desc_1 + sizeof(struct Block_descriptor_1);
struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);

/// 不知道 上面的换成这样能否成立
struct Block_descriptor_2 *desc2 = (struct Block_descriptor_1*)(desc + 1);

另外,这里看到 byref 引用计数加减就是以 2 为单位的,one for caller, one for stack。看不懂。。。。

Contents