返回

深入理解 Block 底层原理

IOS

在 iOS 开发中,Block 是一种非常重要的概念,它允许开发者在运行时将代码块作为参数传递给其他函数或方法。这使得代码更加灵活和可重用。然而,对于 Block 的底层原理,许多开发者并不十分了解。本文将深入剖析 Block 的类型、循环引用的解决方法以及底层实现,帮助开发者更好地理解和使用 Block。

Block 的类型

Block 主要有三种类型:

  • NSGlobalBlock:全局 Block,存储在全局区
  • NSMallocBlock:堆 Block,存储在堆区
  • NSStackBlock:栈 Block,存储在栈区

全局 Block

全局 Block 是在编译时创建的,存储在全局数据区中。由于全局数据区在程序的整个生命周期内都存在,因此全局 Block 也具有相同的生命周期。全局 Block 适用于需要在整个程序生命周期内访问的代码块,例如单例对象的初始化代码。

堆 Block

堆 Block 是在运行时使用 malloc 函数动态分配的,因此存储在堆区中。堆 Block 的生命周期与分配它的对象的生命周期相同。当对象被释放时,堆 Block 也随之被释放。堆 Block 适用于需要在对象生命周期内访问的代码块,例如对象的事件处理函数。

栈 Block

栈 Block 是在栈上创建的,因此存储在栈区中。栈 Block 的生命周期与创建它的函数或方法的生命周期相同。当函数或方法返回时,栈 Block 也随之被销毁。栈 Block 适用于需要在函数或方法执行期间访问的代码块,例如循环体内的代码。

循环引用的解决方法

当 Block 捕获了外部变量时,就可能出现循环引用问题。循环引用是指两个对象相互引用,导致内存无法被释放。在 Block 中,当外部变量强引用了 Block,而 Block 又强引用了外部变量时,就会形成循环引用。

为了解决循环引用问题,可以使用 weakunsafe_unretained 来修饰外部变量。这两个关键字的作用是弱引用外部变量,即不增加外部变量的引用计数。这样,即使 Block 强引用了外部变量,也不会形成循环引用。

weak 关键字

weak 关键字用于修饰外部变量,表示对外部变量的弱引用。当外部变量被释放时,weak 变量会自动被设置为 nil。weak 关键字适用于需要在 Block 中访问外部变量,但又不想形成循环引用的情况。

unsafe_unretained 关键字

unsafe_unretained 关键字也用于修饰外部变量,表示对外部变量的非保留引用。与 weak 关键字不同,unsafe_unretained 变量不会自动被设置为 nil。unsafe_unretained 关键字适用于需要在 Block 中访问外部变量,并且确信外部变量不会被释放的情况。

Block 的底层实现

Block 的底层实现是基于结构体 Block_byrefBlock_literalBlock_byref 结构体存储 Block 的引用计数、函数指针和 captured variables。Block_literal 结构体存储 Block 的代码块。

当一个 Block 被创建时,会创建一个 Block_byref 结构体和一个 Block_literal 结构体。Block_byref 结构体指向 Block_literal 结构体。captured variables 会被拷贝到 Block_byref 结构体中。

当 Block 被调用时,会调用 Block_byref 结构体中的函数指针。函数指针指向 Block_literal 结构体中的代码块。代码块中的 captured variables 会被从 Block_byref 结构体中获取。

结语

Block 是 iOS 开发中一个非常重要的概念,它允许开发者在运行时将代码块作为参数传递给其他函数或方法。理解 Block 的类型、循环引用的解决方法以及底层实现,对于开发者更好地使用 Block 至关重要。本文深入剖析了 Block 的各个方面,希望能够帮助开发者更加熟练地使用 Block,编写出更加优雅高效的代码。