掌握 iOS 內存管理:深入解析 Memory Leak 及其解決策略

Reed Hsin
4 min readMar 17, 2024

--

在 iOS 應用開發中,有效的內存管理是確保應用穩定運行的關鍵。一個常見但又經常被忽視的問題是 Memory Leak,它指的是當應用分配的記憶體在不再需要時未能釋放,導致應用使用的記憶體量持續增加。

這不僅會耗盡系統資源,還可能導致應用性能下降,甚至崩潰。

本文將探討 iOS 開發中產生 Memory Leak 的原因及其解決方法,特別是通過理解 strong reference cycles 和 closures capture list 的管理來避免這一問題。

Strong Reference Cycles 的挑戰與解決方案

Strong Reference Cycles 發生時,兩個或多個 objects 互相保持 strong reference,導致它們無法被釋放。解決這一問題的關鍵在於使用 weakunowned 修飾 references。

Weak 與 Unowned 的選擇

Weak References:被 weak 修飾的 instance object 不會增加 retain count,這意味著它們是可選的(optional),可能為 nil。這適用於那些可能變為 nil 的 instance object,如 delegate 或 dataSource,使用 weak 保持它們不會造成 strong reference cycles。

Unowned References:與 weak 不同,unowned 修飾的 instance object 同樣不會增加 retain count,但它們不能是可選的,這意味著這些 objects 應始終存在,不可能為 nil

如果一個 unowned reference 的 object 被釋放,再次訪問它會導致應用崩潰。因此,unowned 適用於那些生命週期一致的 instance objects。

unowned 本質上是因為 optional 所產生出來的產物,因為相較於訪問那些 weak 修飾的 instance object,系統不用再做 unwrapped 的動作,所以在效率上會更高!

class Department {
var employeeAdded: ((String) -> Void)?
func addEmployee() {
employeeAdded?("Employee added!")
}
}

class Person {
var department: Department
init(department: Department) {
// set department's employeeAdded closure
// we can use unowned here, cause when closure was called
// self(Person instance object) must be existed
department.employeeAdded = { [unowned self] message in
print(message)
}
}
}

Closures Capture List

在 Swift 中,閉包(closures)可能捕獲並保持對其內部使用的外部變量的強引用。

為了避免因此產生的 strong reference cycles,可以在 closure 的 capture list 中使用 [weak xxx][unowned xxx]

同時也可以釐清 local variable, global variable, static variable 是如何被 closure 所捕獲的

有哪些檢測 memory leak 的方式

  1. 簡單的 leak 可以透過 UML diagram 看出

2. 可以使用 Xcode 內建的 Instruments 工具 (Leak 和 Allocation 工具) 來幫找到應用中的記憶體洩漏點

--

--

Reed Hsin

在電商、直撥串流、加密貨幣、社交媒體等領域都有豐富經驗的資深 iOS 工程師,曾在 KKBox、Pinkoi、17Live、Crypto.com,目前於 TikTok 任職。 多年來,我帶領許多非電資背景的學生轉職成為 iOS 工程師,也幫助資深 iOS 工程師轉職到外商 🙌 希望能的經驗,能讓大家在職場上閃閃發光✨