返回

深入剖析 OC 底层之 block

IOS

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,避免潜在的陷阱。