Swift內存管理、weak和unowned以及兩者區別(如何使用Swift 中的weak與unowned?)


Swift 是自動管理內存的,這也就是說,我們不再需要操心內存的申請和分配。

當我們通過初始化創建一個對象時,Swift 會替我們管理和分配內存。而釋放的原則遵循了自動引用計數 (ARC) 的規則:當一個對象沒有引用的時候,其內存將會被自動回收。

這套機制從很大程度上簡化了我們的編碼,我們只需要保證在合適的時候將引用置空 (比如超過作用域,或者手動設為 nil 等),就可以確保內存使用不出現問題。

但是,所有的自動引用計數機制都有一個從理論上無法繞過的限制,那就是循環引用 (retain cycle) 的情況。

 

什么是循環引用

  假設我們有兩個類 A和 B , 它們之中分別有一個存儲屬性持有對方:

class A {
  let b: B
  init() {
    b = B()
    b.a = self
  }
  deinit {
    println("A deinit")
  }
}
class B {
  var a: A? = nil
  deinit {
    println("B deinit")
  }
}

  

  在 A 的初始化方法中,我們生成了一個 B 的實例並將其存儲在屬性中。然后我們又將 A 的實例賦值給了 b.a 。這樣 a.b 和 b.a 將在初始化的時候形成一個引用循環。現在當有第三方的調用初始化了 A ,然后即使立即將其釋放, A 和 B 兩個類實例的 deinit 方法也不會被調用,說明它們並沒有被釋放。

  因為即使 obj 不再持有 A 的這個對象,b 中的 b.a 依然引用着這個對象,導致它無法釋放。而進一步,a 中也持有着 b,導致 b 也無法釋放。在將 obj 設為 nil 之后,我們在代碼里再也拿不到對於這個對象的引用了,所以除非是殺掉整個進程,我們已經 永遠 也無法將它釋放了。多么悲傷的故事啊..

在 Swift 里防止循環引用

  為了防止這種人神共憤的悲劇的發生,我們必須給編譯器一點提示,表明我們不希望它們互相持有。一般來說我們習慣希望 "被動" 的一方不要去持有 "主動" 的一方。在這里 b.a 里對 A 的實例的持有是由 A 的方法設定的,我們在之后直接使用的也是 A 的實例,因此認為 b 是被動的一方。可以將上面的 class B 的聲明改為:

class B {
    weak var a: A? = nil
    deinit {
        println("B deinit")
    }
}

  

  在 var a 前面加上了 weak ,向編譯器說明我們不希望持有 a。這時,當 obj 指向 nil 時,整個環境中就沒有對 A 的這個實例的持有了,於是這個實例可以得到釋放。接着,這個被釋放的實例上對 b 的引用 a.b 也隨着這次釋放結束了作用域,所以 b 的引用也將歸零,得到釋放。添加 weak 后的輸出:

A deinit
B deinit

weak和unowned

      在 Swift 中除了 weak 以外,還有另一個沖着編譯器叫喊着類似的 "不要引用我" 的標識符,那就是 unowned 。它們的區別在哪里呢?如果您是一直寫 Objective-C 過來的,那么從表面的行為上來說 unowned 更像以前的 unsafe_unretained ,而 weak 就是以前的 weak 。

  用通俗的話說,就是 unowned設置以后即使它原來引用的內容已經被釋放了,它仍然會保持對被已經釋放了的對象的一個 "無效的" 引用,它不能是 Optional 值,也不會被指向 nil 。如果你嘗試調用這個引用的方法或者訪問成員屬性的話,程序就會崩潰。而 weak 則友好一些,在引用的內容被釋放后,標記為 weak 的成員將會自動地變成 nil (因此被標記為 @ weak 的變量一定需要是 Optional 值)。

  關於兩者使用的選擇,Apple 給我們的建議是如果能夠確定在訪問時不會已被釋放的話,盡量使用 unowned ,如果存在被釋放的可能,那就選擇用 weak 。

 

日常工作中一般使用弱引用的最常見的場景有兩個:

  1. 設置 delegate 時
  2. 在 self 屬性存儲為閉包時,其中擁有對 self 引用時

 

其中,最常見的delegate使用時,可參考我這篇文章:

Swift代理造成內存泄漏的解決辦法

 

 

所以,weak使用上是更推薦一點的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM