Block 的 Block_descriptor_1
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};