弱引用和无主引用有什么区别?
理解 iOS 内存管理对于 iOS 和 macOS 开发至关重要。弱引用和无主引用的概念比较难理解,尤其对于初学者而言。ARC(自动引用计数)或许已经为我们解决了许多问题。但 Swift 仍然要求您在不使用值类型时经常管理引用。
ARC 或自动引用计数
自动引用计数 (ARC) 用于跟踪和管理应用程序的内存使用情况。在大多数情况下,这意味着内存管理在 Swift 中"正常工作",您无需自行考虑内存管理。当类实例不再需要时,ARC 会自动释放它们占用的内存。
请注意,在 Swift 中,默认情况下,引用被视为强引用。
import UIKit
class ProductListController: UIViewController {
// 创建对 ProductListDataSource() 实例的强引用。当 ProductListController 实例被释放时,它将 ProductListDataSource 实例的引用计数减少 1。
let dataSource = ProductListDataSource()
override func viewDidLoad() {
super.viewDidLoad()
}
}
在上面的例子中,ProductListController 类具有 ProductListDataSource() 类型的强引用。
当将强引用赋值给一个对象时,该对象的引用计数会增加 1。当从该对象中移除强引用时,该对象的引用计数会减少 1。
强引用是什么意思?
我们先来了解一下什么是强引用。它本质上是一个默认引用(包括指针等),但它本身就很特殊,因为它通过将引用计数增加 1 来保护被引用的对象不被 ARC 释放。本质上,只要任何对象对某个对象有强引用,它就不会被释放。这一点在我稍后解释循环引用和其他概念时需要记住。
Swift 代码库几乎完全使用强引用。所有属性声明默认都是强引用。一般来说,当对象层次结构关系呈线性时,可以使用强引用。当对象的层次结构从父级到子级时,依赖强引用通常是明智的。
UIView.animate(withDuration: 0.5) {
self.view.alpha = 0.5
}
由于 animateWithDuration 是 UIView 上的静态方法,因此这里的闭包是父级,而 self 是子级。
弱引用
弱引用是 Swift 中解决循环引用问题的方法之一。需要注意的是,弱引用不会增加或减少对象的引用计数。即使 ARC 的引用计数大于 1,它们也会被释放。
基本上,我们在 Swift 中使用weak关键字将引用标记为弱。此外,弱引用不能使用 let 关键字声明,因为它最终会被传递给 nil。这是因为对象在弱引用指向它时可能会被释放。在你认为可能产生循环引用的代码中,应该存在弱引用。
如何使用弱引用修复循环引用
我们将通过一个示例来了解如何修复循环引用。我们将创建一个数据源模型类,用于通过网络请求获取初始数据。
import UIKit
class ProductListDataSource {
func loadInitialData(_ completion: ((_ isSuccess: Bool) -> ())?) {
// 假设执行网络请求以获取初始数据
// 任务完成后返回完成处理程序
completion?(true)
}
}
class ProductListController: UIViewController {
// 创建了对 ProductListDataSource() 实例的强引用。当 ProductListController 实例被释放时,ProductListDataSource 实例的引用计数会减少 1。
let dataSource = ProductListDataSource()
override func viewDidLoad() {
super.viewDidLoad()
/*
1. Calling the loadInitialData function no longer creates a retain cycle.
2. Self (ProductListController) has a strong reference to the data source.
3. Completion handler has a weak reference to self (ProductListController).
*/
dataSource.loadInitialData { [weak self] isSuccess in
guard let self = self else { return }
// perform tasks that need to be done after successfully fetching data
}
}
}
在上面的例子中,您可以看到我们使用了 self (ProductListController()) 的弱引用,因为一旦从服务器获取数据,它可能需要执行某些操作。
Swift 中的无主引用
无主引用与弱引用非常相似,但有一个区别。无主引用可以保证变量在被访问时不会为 nil。
与弱引用类似,无主引用不会增加或减少对象的引用计数。我们可以说这是修复循环引用的另一种解决方案。当引用指向的实例为 nil 时访问无主引用将导致致命的程序错误。
两者之间有什么区别?
弱引用是一种不会阻止被引用对象被 Swift 运行时释放的引用。
另一方面,无主引用是指不会强行控制被引用对象的引用。然而,与弱引用不同,无主引用被假定始终具有值。
总结两者的区别 −
弱引用和无主引用的区别
| Weak 弱引用 | Unowned 无主引用 |
|---|---|
| 它可以声明为可选的。 | 它不能声明为可选的。 |
| 它随时可能变为 nil。 | 它随时都不能变为 nil。 |
| 它可以阻止对象被释放。 | 它不能阻止对象被释放。 |
| 访问已释放的引用对象时发生运行时错误的几率较低。 | 访问已释放的引用对象时发生运行时错误的几率较高。 |
哪种引用比较合适?
使用弱引用
当两个对象之间存在某种关系,其中一个对象不应该对另一个对象有很强的控制力时,你应该使用弱引用来阻止保留循环。当两个对象相互强引用时,会产生循环引用,从而阻止 Swift 运行时释放任一对象。
例如,在两个对象之间建立父子关系时,通常会使用弱引用作为子对象对父对象的引用,以避免子对象永久维护父对象。
使用无主引用
当两个对象之间的关系中,其中一个对象对另一个对象没有强引用,但您确信被引用的对象在引用对象的整个生命周期内始终存在时,应该使用无主引用。
例如,如果视图控制器持续呈现相同的视图,则可以对视图控制器对视图的引用使用无主引用,因为只要视图控制器存在,视图就会始终存在。
结论
总而言之,这两种引用在各自的场景中都发挥着重要作用。但大多数情况下,我们在应用程序中使用的是弱引用。这是因为弱引用在内存管理方面更优。由于弱引用可以声明为可选类型,并且可以处理 nil,因此建议使用它们。