弱引用和無主引用之間有什麼區別?


瞭解 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+ 瀏覽量

啟動你的 職業生涯

透過完成課程獲得認證

開始學習
廣告

© . All rights reserved.