Strong, Weak and Unowned - 介紹
Manual Reference Count - (MRC) / Automatic Reference Counting - (ARC)
這篇在介紹swift裡記憶體的管理機制
早前的手機,因為硬體資源的限制,對於記憶體的使用格外的珍惜,早期在 Objective-C / Xcode 4 的年代,每個變數的引用與操作 alloc、new、retain、release 都要手動的一個個計數,在撰寫程式時,以良好的習慣,儘可能地釋放出更多記憶體供程式運行。當然,萬一呼叫到已經釋放掉的變數,就直接 Crash,不好好釋放的話,或是變數之間互相引用,造成循環引用,而導致變數在需要被釋放時被咬住而無法釋放(Retain Cycle), 一旦記憶體堆疊到不夠用了 (Memory leak),也是直接 Crash。
隨著硬體的進步,Apple 也慢慢將手動參考計數 轉往自動參考計數,慢慢地由autorelease,再演化成由系統透過 Strong, Weak , Unowned 來判斷記憶體回收的時機
比較表
Strong | Weak | Unowned | |
---|---|---|---|
Reference Count | O | ||
Optional | O | O | |
Non-Optional | O | O | |
var | O | O | O |
let | O | O |
說明
- 只有 Strong 會增加 Reference Count
- Strong / Weak 可以是 nil, 但是 unowned 不能為nil
- Weak 不能用 let 宣告,因為在引用的過程中避免循環,所以只能讀 不能寫
來個常見網路使用的範例
//呼叫API 完成後用completion(trailing closure) 回傳後繼續
class API {
var completion: ((Data?, Error?) -> Void) = { (_,_) in }
}
//給一個 ViewController 長成醬子
class VC: UIViewController {
let api = API()
func loading(_ show:Bool){}
func loadAPI() {
loading(true)
api.completion = { (data, error) in
loading(false) //這裡會出錯
}
}
}
這是個常見的例子 我們在讀取API的前和後,想要秀出轉轉轉的 UIActivityIndicator 讓使用者知道現在正在讀取API,在API讀完之後,將 UIActivityIndicator 設定會隱藏。
在閉包的程式區塊裡,想要使用外部的變數,或是使用外部的函數,Xcode 會提示你加一個 self.
func loadAPI() {
loading(true)
api.completion = { (data, error) in
self.loading(false) //這裡會出錯
}
}
在前面,用以得知,這個是由上一層來的。又因為 Closure 是 Reference Type, 在閉包裡的參數變化,外部的變數值也會跟著變化,因此, 這裡的 Xcode 其實是會報錯的,閉包會把 self.loading 咬住而讓 VC 無法被釋放。
正確的做法是在閉包裡,對於 self 的引用,由 strong 改為 weak / optional type
func loadAPI() {
loading(true)
api.completion = { [weak self] (data, error) in
self?.loading(false)
}
}
如此一來,當外部的 self 不存在時 閉包裡的 loading 也不會強迫使用 外部的 self
那?有沒有其他的解法?
func loadAPI() {
loading(true)
api.completion = { [unowned self] (data, error) in
self.loading(false)
}
}
使用 unowned 時, self 後不用加 ?
例外?
這是一個 animate 的 closure, 因為要對 view 做透明度的變化,在閉包裡的 view ,要在前面加一個 self, 那麼為什麼不用在閉包裡宣告 weak 或是 unowned ? 那是因為 view 的生命週期,只執行一次,不會留存在記憶體裡面重覆使用,因此 沒有 refernece count,自然也沒有 retain cycle 的問題
UIView.animate(withDuration: 1.0) {
self.view.alpha = 0.5
}
轉載請註明來源,歡迎對文章中的引用來源進行考證,歡迎指出任何有錯誤或不夠清晰的表達。可以郵件至 [email protected]