弱引用和无主引用之间有什么区别?


了解 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,因此建议使用它们。

更新于: 2023年3月24日

5K+ 浏览量

启动你的 职业生涯

通过完成课程获得认证

开始学习
广告