深入剖析 OC 底层之 block
2023-12-21 21:33:59
block,一种极其强大的语法糖,它为我们提供了轻量级的代码块,使得我们可以将代码封装成一个对象,在其他地方进行传递和调用。在本文中,我们将深入探索 block 的底层实现,揭开其神秘面纱。
block 的类型
block 有三种类型:栈 block(NSStackBlock)、堆 block(NSMallocBlock)、全局 block(NSGlobalBlock)。
- 栈 block(NSStackBlock) :存储在栈上的 block,通常用于在函数内部定义的小型、临时代码块。
- 堆 block(NSMallocBlock) :存储在堆上的 block,通常用于需要在函数外部捕获变量的长期存在的代码块。
- 全局 block(NSGlobalBlock) :存储在只读数据段中的 block,通常用于需要在整个应用程序生命周期中存在的代码块。
block 的实现
block 的底层实现是一个结构体,它包含了以下成员:
- isa:指向 block 类的指针,用于识别 block 的类型。
- flags:包含有关 block 的各种标志,例如是否具有捕获列表、是否可变等。
- reserved:保留的字段,用于未来的扩展。
- invoke:指向 block 实现函数的指针。
- descriptor: block 的元数据,包括捕获变量的类型和数量。
- size:block 的大小。
- copy_helper:指向复制 block 函数的指针。
- dispose_helper:指向销毁 block 函数的指针。
block 的创建
可以使用 ^ 语法或 block_copy() 函数创建 block。^ 语法会创建栈 block 或堆 block,具体取决于 block 是否捕获变量。block_copy() 函数用于复制现有的 block。
block 的调用
可以使用 () 语法调用 block。block 调用会跳到 invoke 成员函数,该函数执行 block 中的代码。
block 的捕获列表
捕获列表是 block 中捕获外部变量的列表。当 block 离开其创建范围时,这些变量将被复制到 block 中。捕获列表中的变量必须是强引用,否则它们可能会被释放,从而导致 block 崩溃。
block 的循环引用
当 block 捕获一个强引用到自身时,就会发生循环引用。这可能会导致内存泄漏,因为 block 永远不会被释放。为了避免循环引用,可以使用 __weak 或 __unsafe_unretained 修饰符来弱引用自身。
block 的性能
block 通常比函数调用快,因为它们避免了函数调用开销。然而,如果 block 捕获大量变量,则性能可能会受到影响。
结论
block 是一个强大的工具,可以帮助我们编写更灵活、更简洁的代码。通过了解其底层实现,我们可以更有效地使用 block,避免潜在的陷阱。