返回
iOS 底层揭秘:纵横捭阖,剖析 Block(四)——探索 Block 的底层奥秘与源码探析
IOS
2023-09-08 14:36:28
Block 的底层奥秘:深入探索其原理和机制
在揭开 Block 的神秘面纱后,我们不禁好奇它的内部构造和运行机制。踏上探索之旅,让我们深入分析 Block 的底层原理,发掘其内在的奥秘。
Block 的结构解剖
从源码中,Block 的结构显露无遗:
struct Block_layout {
void (*invoke)(void *, ...);
struct Block_descriptor_1 *descriptor;
int flags;
int reserved;
void *captured[1];
};
仔细审视这个结构体,我们发现 Block 的关键组成部分:
invoke
函数指针: 指向 Block 实现代码的地址。descriptor
指针: 指向 Block 符,包含元数据。flags
: 标记 Block 的属性,如可捕获性、可变性。reserved
: 保留字段。captured
数组: 用于捕获外部变量。
Block 的类型演变
根据捕获变量的方式,Block 分化出不同的类型:
- 全局 Block: 无外捕获,仅在栈上分配。
- 栈 Block: 捕获栈变量,但不可修改。
- 堆 Block: 捕获栈变量,并可修改。
Block 的调用机制
当调用一个 Block 时,系统会采取以下步骤:
- 检查 Block 的
flags
,确定其类型。 - 全局 Block 直接调用
invoke
函数。 - 栈 Block 或堆 Block 创建副本后,再调用副本的
invoke
函数。 - Block 的
invoke
函数执行代码,访问捕获变量。
源码细读:Block 的实现原理
深入 Block 的源码,我们发现它的实现精妙:
在头文件 Block_private.h
中,定义了 Block 符的结构体:
struct Block_descriptor_1 {
unsigned long int reserved;
unsigned long int size;
const char *copy_helper;
const char *dispose_helper;
const char *invoke;
const char *layout;
};
其成员包含 Block 的元数据,如大小、复制、释放和调用帮助函数。
在源文件 Block_private.c
中,实现了 Block 的各种操作函数:
static inline void *
Block_copy(const void *arg)
{
Block_layout *block = (Block_layout *)arg;
Block_layout *newBlock =
Block_copy_create(Block_size(block), block->invoke,
block->descriptor);
memcpy(newBlock, arg, Block_size(block));
return newBlock;
}
Block_copy()
函数负责创建 Block 副本,复制内容到新副本中。
总结:掌控 Block 的真谛
通过揭开 Block 的底层原理和源码,我们深入理解了其内部结构、类型、调用机制。这些知识将帮助我们在 iOS 开发中游刃有余地使用 Block,提升编程效率。
Block 使用中的常见问题解答
问:如何捕获外部变量?
答:在 Block 的 captured
数组中声明变量。
问:如何复制 Block?
答:使用 Block_copy()
函数创建副本。
问:何时使用堆 Block?
答:当需要修改捕获的变量时。
问:Block 的调用机制有哪些限制?
答:复制 Block 会引入开销,而堆 Block 的释放需要手动管理。
问:如何高效使用 Block?
答:考虑 Block 的类型和调用成本,并避免过度捕获变量。