Contents

Block_descriptor 是描述 Block 自身信息的,当 Block 需要 retain、release 的时候使用什么函数(即描述 block 本身需要被 retain 和 release 时采用哪种方式),Block 是否捕获变量,变量是否是基本类型都会影响最后使用的 Block_descriptor。

以源码为例

#import "MyObj.h"

typedef int(^Block)(int a);

@interface MyObj ()

@property (nonatomic, assign) Block block;

@end

@implementation MyObj {
    NSUInteger bu_haha;
}

- (void)handleIvar {
    NSObject *obj = nil;
    
    int i = 0;
    Block block = ^(int a) {
        return i;
    };
    
    self.block = block;
}

@end

block 定义的内存结构为

struct Block_layout {
    void *isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor;
    // imported variables
};

descriptor 的结构定义了三种:Block_descriptor_1,Block_descriptor_2,Block_descriptor_3。分别对应常规、捕获对象

struct Block_descriptor_1 {
    uintptr_t reserved;
    uintptr_t size;
};

struct Block_descriptor_2 {
    // requires BLOCK_HAS_COPY_DISPOSE
    BlockCopyFunction copy;
    BlockDisposeFunction dispose;
};

struct Block_descriptor_3 {
    // requires BLOCK_HAS_SIGNATURE
    const char *signature;
    const char *layout;     // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};

如果 block 内部捕获了对象就选用 Block_descriptor_1 和 Block_descriptor_2

static struct __MyObj__handleIvar_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __MyObj__handleIvar_block_impl_0*, struct __MyObj__handleIvar_block_impl_0*);
  void (*dispose)(struct __MyObj__handleIvar_block_impl_0*);
} __MyObj__handleIvar_block_desc_0_DATA = { 0, sizeof(struct __MyObj__handleIvar_block_impl_0), __MyObj__handleIvar_block_copy_0, __MyObj__handleIvar_block_dispose_0};

这里有俩函数:__MyObj__handleIvar_block_copy_0__MyObj__handleIvar_block_dispose_0

static void __MyObj__handleIvar_block_copy_0(struct __MyObj__handleIvar_block_impl_0*dst, struct __MyObj__handleIvar_block_impl_0*src) {
    _Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
  }

static void __MyObj__handleIvar_block_dispose_0(struct __MyObj__handleIvar_block_impl_0*src) {
    _Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
  }

如果 block 不捕获对象生成的 desc 就是如下这个 Block_descriptor_1 没有 Block_descriptor_2 里面那俩 copy 和 dispose 函数了

static struct __MyObj__handleIvar_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __MyObj__handleIvar_block_desc_0_DATA = { 0, sizeof(struct __MyObj__handleIvar_block_impl_0)};

源码里有三个 desc 字段的 accessors,其中 _Block_descriptor_1 看懂了。_Block_descriptor_2 一时之间没看懂

#if 0
static struct Block_descriptor_1 * _Block_descriptor_1(struct Block_layout *aBlock)
{
    return aBlock->descriptor;
}
#endif

static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
{
    if (! (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) return NULL;
    uint8_t *desc = (uint8_t *)aBlock->descriptor;
    desc += sizeof(struct Block_descriptor_1);
    return (struct Block_descriptor_2 *)desc;
}

static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
{
    if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
    uint8_t *desc = (uint8_t *)aBlock->descriptor;
    desc += sizeof(struct Block_descriptor_1);
    if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
        desc += sizeof(struct Block_descriptor_2);
    }
    return (struct Block_descriptor_3 *)desc;
}

关键是 desc += sizeof(struct Block_descriptor_1); 这句,细想之后明白了 desc 指向了 block 结构体中的 desc 指针,desc 是个结构体,地址就是
_Block_descriptor_1 结构体的第一个字段地址,把 _Block_descriptor_1 的大小加上去。指针就落到了 _Block_descriptor_1 结构体中最后一个成员的末尾,下一个成员的开始位置。因此就是 _Block_descriptor_2 结构体的首地址,即指针。

当一个 block 要捕获对象时,desc 就长这个样子。

static struct __MyObj__handleIvar_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __MyObj__handleIvar_block_impl_0*, struct __MyObj__handleIvar_block_impl_0*);
  void (*dispose)(struct __MyObj__handleIvar_block_impl_0*);
} __MyObj__handleIvar_block_desc_0_DATA = { 0, sizeof(struct __MyObj__handleIvar_block_impl_0), __MyObj__handleIvar_block_copy_0, __MyObj__handleIvar_block_dispose_0};
Contents