簡體   English   中英

為什么 Swift ARC 不首先中斷屬性引用以防止保留循環?

[英]Why doesn't Swift ARC break property references first to prevent retain cycles?

我只是想知道當代碼塊超出范圍時 ARC 是如何工作的。 我假設所有引用變量都設置為 nil/destroyed 並排除了屬性,並且所有引用計數為 0 的對象都被銷毀。 這個場景:

A = nil
B = nil

或者可能所有的變量和屬性引用從上到下都設置為 nil,這將保留任何保留循環,因為在訪問屬性之前 A 和 B 是 nil。

A = nil
B = nil
A?.macbook = nil
B?.person = nil

所以我只是想知道為什么 ARC 不首先循環遍歷屬性(向后遍歷代碼)以首先刪除屬性引用,這會破壞任何保留循環。

    A?.macbook = nil
    B?.person = nil
    A = nil
    B = nil

我想我了解保留周期的基礎知識,我只是對 ARC 在自動銷毀階段(例如完成函數的執行)期間發生的事情的細節感到好奇。 也許屬性引用沒有被訪問/銷毀,因為它們是對象的一部分? 也許檢查每個屬性在計算上太復雜了?

       var name: String
       init(name: String) {
           self.name = name
           print("The Person \(name) is born")
       }
       var macbook: Macbook?
       deinit {
           print("The Person \(name) has died")
       }
   }
   
   class Macbook {
       var model: String
       init(model: String) {
           self.model = model
           print("The \(model) Macbook is born")
       }
       var person: Person?
       deinit {
           print("The \(model) Macbook has expired")
       }
   }
   
   func runTasks() {
       var A: Person? = Person(name: "Reuben")
       var B: Macbook? = Macbook(model: "Pro 2020")
       
       //Setup Retain Cycle
       A?.macbook = B
       B?.person = A
       
       //A?.macbook = nil
       B?.person = nil //**Why doesn't ARC make this nil first to avoid retain cycles?**
       A = nil
       B = nil
   }
   
   runTasks()

你問:

我只是想知道在自動銷毀階段 ARC 會發生什么的細節,例如完成函數的執行。

當您有一個引用對象實例的局部變量時,它會建立對該對象的強引用。 當局部變量超出范圍時,它會釋放其強引用。 如果這是對該對象的最后一個強引用,則該對象將被釋放。

func runTasks() {
    var person = Person(name: "Reuben")
    
    // At this point, the `Person` object has one strong reference, as does the `MacBook`

    doSomething(with: person)

    // The `person` variable falls out of scope, the sole strong reference to the `Person` 
    // instance will be relieved, and the `Person` object will therefore be deallocated.
}

也許屬性引用沒有被訪問/銷毀,因為它們是對象的一部分?

當一個對象被釋放時,它的任何具有對其他事物的強引用的屬性也會自動釋放。

func runTasks() {
    var person = Person(name: "Reuben")
    var computer = Macbook(model: "Pro 2020")
    
    // At this point, the `Person` object has one strong reference, as does the `MacBook`

    person.macbook = computer

    // Now the `Macbook` instance has two strong references, the local variable and the `Person`

    doSomething(with: person)

    // When `person` and `computer` variables the local variables fall out of scope
    // at the end of this function, their respective strong references to the `Person` 
    // and the `Macbook` objects will be released. But since there are no more strong
    // references to `Person`, it will be deallocated. But when it is deallocated,
    // its strong reference to `Macbook` will be released automatically, too. So now,
    // the two strong references to that `Macbook` object are now released, too (both
    // the `computer` local variable and the `macbook` property of `Person`), so it will
    // deallocated, too.
}

現在,正如您所確定的,強引用循環(以前稱為“保留循環”)是當兩個或多個對象保持對彼此的強引用時(因此,除非您手動將一個或多個這些引用置nil ,否則它們將結束相互之間存在揮之不去的強引用,因此在循環中斷之前兩者都不會被釋放)。

內存管理系統在運行時為我們識別和打破這些強引用循環似乎很有吸引力,但這在計算上是不切實際的。 它必須構建一個內存圖,識別哪些對象正在相互引用,並以某種方式找出要中斷的引用。 甚至在某些情況下,我們可能有一些我們絕對不希望操作系統為我們解決的短暫周期(例如,您創建了一個調度隊列,調度了一些工作塊,我們依賴隊列直到分派的塊已完成; URLSession是另一個示例)。

幸運的是,雖然這種常量內存圖分析在運行時不可行,但 Xcode 確實提供了一個調試工具來幫助識別這些場景,即“調試內存圖” 功能 有關更多信息,請參閱如何在 Leaks 工具未顯示內存泄漏時調試內存泄漏? 或者如何在 Swift 中識別強引用循環? 帶有 ARC 的 iOS 應用程序,查找對象的所有者

幸運的是,雖然我們有很好的診斷工具來找到這些強引用循環,但通過使用weak引用或unowned引用打破循環,首先防止它們很容易。 結果是一個高性能、非常簡單的內存管理基礎設施,其中使用weak / unowned很容易避免循環,但是我們有一些出色的調試工具可以在需要時識別問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM