弱引用和无主引用之间有什么区别?
了解 iOS 内存管理对于 iOS 和 macOS 开发至关重要。弱自身和无主自身的概念难以理解,尤其对于初学者而言。ARC(自动引用计数)可能为我们解决了许多问题。在不使用值类型时,Swift 仍然要求您在很多时候管理引用。
ARC 或自动引用计数
自动引用计数 (ARC) 用于跟踪和管理应用程序的内存使用情况。在大多数情况下,这意味着内存管理在 Swift 中“正常工作”,您无需自己考虑内存管理。ARC 会在不再需要类实例时自动释放类实例使用的内存。
请注意,在 Swift 中,默认情况下,引用被计为强引用。
import UIKit class ProductListController: UIViewController { // Create a strong reference to a ProductListDataSource() instance. When the ProductListController instance is deallocated, it will decrease the reference count of the ProductListDataSource instance by 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) -> ())?) { // assume perform networking request to fetch initial data // returning completion handler after finishing the task completion?(true) } } class ProductListController: UIViewController { // Created a strong reference to a ProductListDataSource() instance. When the ProductListController instance is deallocated, it will decrease the reference count of the ProductListDataSource instance by 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 运行时释放被引用对象的引用。
另一方面,无主引用是指不会对被引用对象保持强引用的引用。但是,与弱引用不同,无主引用被假定始终具有值。
总结两者的区别 −
弱引用和无主引用的区别
弱引用 | 无主引用 |
---|---|
它可以声明为可选的。 | 它不能声明为可选的。 |
它可以随时变为 nil。 | 它不能随时变为 nil。 |
它可以防止对象被释放。 | 它不会防止对象被释放。 |
如果访问已释放的引用对象,则发生运行时错误的可能性较小。 | 如果访问已释放的引用对象,则发生运行时错误的可能性很高。 |
哪个引用适合使用?
使用弱引用
当两个对象之间存在一种关系,其中一个对象不应对另一个对象有强烈的控制时,您应该使用弱引用来停止保留循环。当两个对象相互之间存在强引用时,就会产生保留循环,从而阻止 Swift 运行时释放任何一个对象。
例如,在两个对象之间建立父子关系时,通常会对孩子的对父级的引用使用弱引用,以避免孩子永久保留父级。
使用无主引用
当两个对象之间存在一种关系,其中一个对象不对另一个对象有强烈的控制,但您确信被引用对象将在引用对象的整个生命周期内始终存在时,您应该使用无主引用。
例如,如果视图控制器始终呈现相同的视图,则您可以对视图控制器对视图的引用使用无主引用,因为只要视图控制器存在,视图就会始终存在。
结论
总之,两种引用在各自的场景中都扮演着重要的角色。但在大多数情况下,我们在应用程序中使用弱引用。这是因为弱引用在内存管理方面更好。由于弱引用可以声明为可选的并处理 nil,因此建议使用它们。