源码下载地址: Open Source
我使用的版本: libclosure-74.tar
这篇文章阅读前,先看编译后的 block 对理解会有帮助: 其他: clang 编译后的的 block 解析
之前在编译后的代码中提到过, block 本质是一个结构体
生成的结构体,在源码中对应的是 struct Block_layout
(ps.个人推测, 编译后的结构体是用源码中的结构体由编译拼接出来的)
数据结构

虽然类似的图以及在很多文章出现过了,但还是自己画一遍加深印象. 整体结构非常简单
Block_layout
就是 block 被编译后的数据结构, 其中 isa 的指向源码中没有提供(只有几个数组)
isa根据存储的内存区域(堆,栈,全局)不同而有不同的值(应该没开源)
(源码中定义是个数组, 使用时也指向这个数组,很奇怪)flags: 下图的匿名结构体(注意这个 enum 可以同时是几个值的合体, 看到位控制了没,不同的值只要取不同 bit 就可以都表示)- 其中
BLOCK_NEEDS_FREE代表需要释放, 从后面函数中使用来看, 就是放在堆的意思 BLOCK_HAS_COPY_DISPOSE代表有 copy 和 dispose 函数, 就是 descriptor2 中那两个字段
- 其中
invoke: 函数指针, 指向代码中定义 block 函数体(编译后的 block 函数体会被定义成全局函数)descriptor: 一些描述信息 (里面带了变量的复制和销毁函数)- 可以看到这里被分为 1,2,3 分别存了不同的信息
- 这些形式在实际使用中,通过 flag 配合内部地址偏移可以访问到
- 2 对应flag
BLOCK_HAS_COPY_DISPOSE, 3 对应 flagBLOCK_HAS_SIGNATURE
imported variables: 我们在编译后的代码中看到其他: clang 编译后的的 block 解析, 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
};
enum {
BLOCK_DEALLOCATING = (0x0001), // runtime
BLOCK_REFCOUNT_MASK = (0xfffe), // runtime
BLOCK_NEEDS_FREE = (1 << 24), // runtime
BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler
BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code
BLOCK_IS_GC = (1 << 27), // runtime
BLOCK_IS_GLOBAL = (1 << 28), // compiler
BLOCK_USE_STRET = (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30), // compiler
BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler
};
Block_byref
之前探究编译后结果的文章其他: clang 编译后的的 block 解析, 基本数据类型要在 block 中被修改. 需要加上__block 标记.
这个标记加上后, 编译的结果就是基本数据类型被封装成struct Block_byref
ps.下面的结构体应该比实际生成的结构体是要少了 基本数据类型本身 这个字段(比如前文的 int mainInt 也在这个结构体中)
isa:forwarding: 在栈中指向自己, 其使用和 copy 有关flags: 既下面的enum(注意这个 enum 可以同时是几个值的合体)
struct Block_byref {
void *isa;
struct Block_byref *forwarding;
volatile int32_t flags; // contains ref count
uint32_t size;
};
// Values for Block_byref->flags to describe __block variables
enum {
// Byref refcount must use the same bits as Block_layout's refcount.
// BLOCK_DEALLOCATING = (0x0001), // runtime
// BLOCK_REFCOUNT_MASK = (0xfffe), // runtime
BLOCK_BYREF_LAYOUT_MASK = (0xf << 28), // compiler
BLOCK_BYREF_LAYOUT_EXTENDED = ( 1 << 28), // compiler
BLOCK_BYREF_LAYOUT_NON_OBJECT = ( 2 << 28), // compiler
BLOCK_BYREF_LAYOUT_STRONG = ( 3 << 28), // compiler
BLOCK_BYREF_LAYOUT_WEAK = ( 4 << 28), // compiler
BLOCK_BYREF_LAYOUT_UNRETAINED = ( 5 << 28), // compiler
BLOCK_BYREF_IS_GC = ( 1 << 27), // runtime
BLOCK_BYREF_HAS_COPY_DISPOSE = ( 1 << 25), // compiler
BLOCK_BYREF_NEEDS_FREE = ( 1 << 24), // runtime
};
使用的函数
_Block_copy: 对 Block 的复制函数
总共 4 个分支(入参空否,处理 BLOCK_NEEDS_FREE, 处理BLOCK_IS_GLOBAL, 处理剩下情况)
- 对要复制的入参判空,
- 判断 flag(前面数据结构的 flags 就是做这个用的)
- 在堆中的 block, 引用计数+1
- 在全局的 block, 直接 return
- 其他情况下, 在堆中申请一块内存, 然后逐个字段赋值, 改 flags,引用计数赋值
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
if (!arg) return NULL;
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
else {
// Its a stack block. Make a copy.
struct Block_layout *result =
(struct Block_layout *)malloc(aBlock->descriptor->size);
if (!result) return NULL;
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
result->invoke = aBlock->invoke;
#endif
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
_Block_call_copy_helper(result, aBlock);
// Set isa last so memory analysis tools see a fully-initialized object.
result->isa = _NSConcreteMallocBlock;
return result;
}
}
_Block_release : 对 Block 的释放函数
判空 -> 判断是不是 global -> 判断是否堆中的 -> 判断引用计数 -> 调用 dispose_helper(销毁内部的变量) -> 释放
void _Block_release(const void *arg) {
struct Block_layout *aBlock = (struct Block_layout *)arg;
if (!aBlock) return;
if (aBlock->flags & BLOCK_IS_GLOBAL) return;
if (! (aBlock->flags & BLOCK_NEEDS_FREE)) return;
if (latching_decr_int_should_deallocate(&aBlock->flags)) {
_Block_call_dispose_helper(aBlock);
_Block_destructInstance(aBlock);
free(aBlock);
}
}
_Block_object_assign: 对 block 中参数的复制操作
_Block_object_dispose: 对 block 中参数的销毁操作
内部都是 switch-case,用于对不同的外部变量做内部的复制和释放处理用. 前面数据结构的flags 就用来标记这个
源码没啥内容, 直接上源码吧,就不贴了
runtime.cpp