你知道什麼是 Retain cycle 嗎?

有一些朋友會與我分享初次接觸 iOS 開發的喜悅,但在分享的同時,我常常會好奇的問一句

你知道 retain cycle 嗎?

所謂的 retain cycle 就是 Block 和 object 可能會互相強引用,互相retain對方,這樣就導致了retain cycle,最後這個 Block 和 object 就變成了孤島,誰也釋放不了誰
參考網站:http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/

我認為不懂 retain cycle,不算是一個會寫 iOS 的工程師,為什麼呢?

因為 App 會因為 retain cycle 而 memory leak,進一步造成 Crash,而這個問題你卻不知道怎麼產生的

這應該是 iOS 特有的問題,這跟它的記憶體回收機制有關 – retain count

常常有人會跟我說,現在都用 ARC 啦,不用在意這個,或者是我每隔一段時間都會把記憶體全釋放掉,沒有這個問題啦

出人意料的是,retain cycle 不論是在 MRC、ARC 都會發生,而且一旦達成條件,除非把 App kill 掉,否則是無法釋放記憶體的。

它麻煩的地方在於,開發時很難發現,編譯器不會告訴你錯在哪裡,因為那是在執行時期才會發生的問題。而且不會立即發生,memory leak 要等到記憶體用光 App 才會 Crash,而這個時候,通常都是 App 上架之後的事了。

為了避免 retain cycle,我們有必要知道什麼狀況下會發生,最常見的地方分別是使用 delegate 和 block/closure 的時候,當我們使用到這兩種寫法時,就得特別注意,網路上很多相關文章,在此不再贅述

參考網站:iOS blocks – 三個會造成retain cycle的anti patterns

在 Swift 通常會使用兩種修飾字來避開 retain cycle,分別是 weak 和 unowned,這兩者表示不對物件進行 retain 的動作,其差別在於 weak 在記憶體被釋放後 pointer 會指向 nil,unowned 則不會,由於 iOS 在 runtime 會對 nil 進行處理,對一個為 nil 的物件呼叫其方法是不會 crash 的。

在 Objective-C,unowned 相當於 Unsafe_unretian 或 assign,這三者修飾的變數若在 dealloc 之後繼續使用,將會造成 dangling pointer。
參考網站:http://stackoverflow.com/questions/29605688/unsafe-unretain-vs-weak-vs-assign

因此在某 block 使用該變數時,確認它的存活週期會大於 block,那就可以放心的使用 unowned,否則還是用 weak 比較保險。

要解決 retain cycle 的最好思維就是想清楚從屬關係,但就算有了這個觀念,但有時會遇到疑似發生的情況,卻又無法確定該怎麼辦?

這時我們可以使用內建工具 instrument,來檢查是否有 memory leak 的狀況發生。
參考網站:Instruments Tutorial with Swift: Getting Started

在這篇文章中,提到會造成 memory leak 的狀況有兩種
Allocations:偵測記憶體無限增加的狀況
Leaks:偵測記憶體沒有正常釋放的狀況

我們要用的是第二種檢測工具 Leaks,只要重複操作你覺得有可能會造成記憶體漏水的地方,很快就會偵測到。

以上就是我對 retain cycle 的分享,歡迎大家與我討論或給予建議,謝謝。

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *