Swift Interview Deep Dive #02

更新 發佈閱讀 9 分鐘


ARC 與 Memory Leak:為什麼 Swift 還是會記憶體洩漏?

很多人第一次學 Swift 時,都會以為:

Swift 有 ARC,所以不會 memory leak。

但實際上,只要你做過中大型 iOS 專案,你很快就會發現:

Swift 不但會 leak,而且很容易 leak。

甚至很多 crash、效能問題、畫面卡頓、ViewController 無法釋放,本質上都跟:

memory ownership

有關。

而 ARC 這題在面試真正想考的,也從來不是:

  • strong / weak / unowned 的定義
  • retain count 怎麼加減

而是:

你是否理解「物件的生命週期(lifecycle)」與「所有權(ownership)」。

1. ARC 到底是什麼?

ARC(Automatic Reference Counting)本質上是一套:

自動管理 reference memory 的機制

它的核心概念其實只有一句話:

只要還有人持有你,你就不能被釋放

例如:

class User {
deinit {
print("deinit")
}
}
var user: User? = User()
user = nil

結果:

deinit

因為:

  • reference count = 1 → 建立時
  • reference count = 0 → user = nil

所以 ARC 幫你釋放記憶體。

2. 真正的問題:ARC 不知道「你該不該互相持有」

ARC 很聰明。

但它不知道:

這兩個 object 到底是不是應該互相擁有

這就是 memory leak 的起點。

3. 最經典的 Retain Cycle

class Person {
var apartment: Apartment?
}
class Apartment {
var tenant: Person?
}

建立:

var wayne: Person? = Person()
var room: Apartment? = Apartment()
wayne?.apartment = room
room?.tenant = wayne

看起來沒問題。

但:

wayne = nil
room = nil

你會發現:

deinit 沒被呼叫

因為:

Person 強持有 Apartment
Apartment 又強持有 Person

形成:

Retain Cycle

4. ARC 不會幫你解決設計錯誤

這是很多初學者最大的誤解。

ARC 的工作不是:

判斷誰該活著

ARC 的工作只是:

計算還有沒有人持有你

所以如果你設計成:

大家互相強引用

ARC 只會覺得:

那你們就一起永生吧

5. 為什麼 Closure 是 Memory Leak 最大來源?

因為 Closure 本質上也是:

Reference Type

而且它會:

捕獲(capture)外部變數

看這段:

class ViewModel {

var onUpdate: (() -> Void)?

func fetch() {
onUpdate = {
self.reload()
}
}
func reload() {
print("reload")
}
deinit {
print("deinit")
}
}

問題在哪?

其實是:

self 強持有 closure
closure 又強持有 self

變成:

self → closure → self

這是 iOS 最常見的 leak。

6. weak self 真正的意義

很多人背:

[weak self]

但不知道原因。

真正的原因是:

你在告訴 closure:
不要擁有我

改成:

onUpdate = { [weak self] in
self?.reload()
}

現在:

  • closure 不再強持有 self
  • retain cycle 被打破

所以:

ViewModel 可以正常釋放

7. weak vs unowned 到底差在哪?

這題超常考。

但大多數人只會背:

  • weak 是 optional
  • unowned 不是 optional

真正的差異其實是:

weak: 可以變 nil, 安全, 適合生命週期不確定

unowned: 假設一定存在, 效能略高, 適合生命週期綁定

weak

weak var delegate: MyDelegate?

意思是:

對方可能先死掉

所以要 optional。

unowned

unowned let owner: Owner

意思是:

我相信 owner 一定活著

如果 owner 已經釋放:

直接 crash

8. Delegate 為什麼幾乎都要 weak?

這題其實在考:

誰擁有誰?

通常:

ViewController 擁有 Child

所以:

Child 不應該反過來強持有 ViewController

否則:

雙向 ownership

就 leak 了。

9. SwiftUI 為什麼 leak 問題比較少?

因為 SwiftUI 的核心是:

Value-driven UI

View 幾乎都是:

struct

所以:

  • 不存在 retain cycle
  • 不共享 reference
  • 沒有 object graph

真正容易 leak 的地方反而是:

  • ObservableObject
  • async task
  • timer
  • notification
  • Combine subscription

因為這些:

又回到 Reference World

10. Async/Await 時代的新 Leak

很多人以為:

用了 async/await 就安全

其實沒有。

例如:

Task {
await self.fetch()
}

Task 會:

強持有 self

如果:

  • task 很久
  • view 已離開
  • task 沒取消

就可能:

ViewModel 永遠不釋放

這也是為什麼現在越來越強調:

  • Task cancellation
  • structured concurrency
  • actor isolation

11. Memory Leak 真正恐怖的地方

Leak 最可怕的不是:

記憶體變多

而是:

舊狀態還活著

例如:

  • 舊 ViewModel 沒釋放
  • 舊 observer 還在
  • 舊 network callback 還在
  • 舊 state 還在更新 UI

最後你會看到:

「明明畫面都關掉了,為什麼還在跑?」

12. 面試真正想測的是什麼?

ARC 這題真正考的是:

你有沒有 ownership 思維

也就是:

  • 誰該擁有誰
  • 誰該活多久
  • 誰該負責 lifecycle
  • 哪裡可能產生循環引用

真正成熟的工程師,不會只想:

怎麼避免 leak

而是會先想:

ownership 應該怎麼設計

結論

ARC 從來不是:

自動記憶體管理

它真正管理的是:

Reference Ownership

而 Memory Leak 本質上也不是 bug。

它通常代表:

你的 object relationship 設計出了問題。

延伸思考

當你開始深入:

  • SwiftUI
  • Combine
  • Async/Await
  • Actor
  • TCA

你會發現:

Swift 其實正在慢慢減少:

共享 reference state

因為:

Shared mutable state 幾乎就是 complexity 的來源。

而 ARC,只是讓你開始看見這件事的入口而已。

留言
avatar-img
WL的沙龍
0會員
5內容數
Swift • SwiftUI • iOS Focus on state, architecture, and system design.
你可能也想看
Thumbnail
見諸參與鄧伯宸口述,鄧湘庭於〈那個大霧的時代〉記述父親回憶,鄧伯宸因故遭受牽連,而案件核心的三人,在鄧伯宸記憶裡:「成立了成大共產黨,他們製作了五星徽章,印刷共產黨宣言——刻鋼板的——他們收集中共空飄的傳單,以及中國共產黨中央委員會有關文化大革命決議文的英文打字稿,另外還有手槍子彈十發。」
Thumbnail
見諸參與鄧伯宸口述,鄧湘庭於〈那個大霧的時代〉記述父親回憶,鄧伯宸因故遭受牽連,而案件核心的三人,在鄧伯宸記憶裡:「成立了成大共產黨,他們製作了五星徽章,印刷共產黨宣言——刻鋼板的——他們收集中共空飄的傳單,以及中國共產黨中央委員會有關文化大革命決議文的英文打字稿,另外還有手槍子彈十發。」
Thumbnail
此章節旨在解釋Swift語言中函數的基本結構和操作方式,包括函數的聲明、呼叫、參數和返回值。閱讀這個章節可以幫助你理解並掌握如何在Swift編程中有效地使用和管理函數。
Thumbnail
此章節旨在解釋Swift語言中函數的基本結構和操作方式,包括函數的聲明、呼叫、參數和返回值。閱讀這個章節可以幫助你理解並掌握如何在Swift編程中有效地使用和管理函數。
Thumbnail
當代名導基里爾.賽勒布倫尼科夫身兼電影、劇場與歌劇導演,其作品流動著強烈的反叛與詩意。在俄烏戰爭爆發後,他持續以創作回應專制體制的壓迫。《傳奇:帕拉贊諾夫的十段殘篇》致敬蘇聯電影大師帕拉贊諾夫。本文作者透過媒介本質的分析,解構賽勒布倫尼科夫如何利用影劇雙棲的特質,在荒謬世道中尋找藝術的「生存之道」。
Thumbnail
當代名導基里爾.賽勒布倫尼科夫身兼電影、劇場與歌劇導演,其作品流動著強烈的反叛與詩意。在俄烏戰爭爆發後,他持續以創作回應專制體制的壓迫。《傳奇:帕拉贊諾夫的十段殘篇》致敬蘇聯電影大師帕拉贊諾夫。本文作者透過媒介本質的分析,解構賽勒布倫尼科夫如何利用影劇雙棲的特質,在荒謬世道中尋找藝術的「生存之道」。
Thumbnail
本篇介紹了Swift程式語言中的各種流程控制元素,包括條件語句(如if, else if, else),三元運算子,多條件分支判斷的switch語句,以及各種迴圈(如for迴圈,while迴圈,以及repeat-while迴圈)。同時也詳細解釋了如何進行迴圈嵌套,以及如何使用控制迴圈語句。
Thumbnail
本篇介紹了Swift程式語言中的各種流程控制元素,包括條件語句(如if, else if, else),三元運算子,多條件分支判斷的switch語句,以及各種迴圈(如for迴圈,while迴圈,以及repeat-while迴圈)。同時也詳細解釋了如何進行迴圈嵌套,以及如何使用控制迴圈語句。
Thumbnail
本文檔介紹了在Swift中使用套件的詳細方法,包括如何引用第三方套件和自定義模組,如何創建自定義套件,以及一些常見的Swift套件。這些套件可以幫助開發者快速添加功能到項目中,提高開發效率和程式碼品質。
Thumbnail
本文檔介紹了在Swift中使用套件的詳細方法,包括如何引用第三方套件和自定義模組,如何創建自定義套件,以及一些常見的Swift套件。這些套件可以幫助開發者快速添加功能到項目中,提高開發效率和程式碼品質。
Thumbnail
本章節介紹了程式語言中的各種運算符,包括算數運算子、比較運算子、賦值運算子、位元運算子,以及運算子的優先等級。每種運算子都有對應的範例程式碼和輸出結果,以幫助讀者更好地理解其用法和效果。此外,章節也強調了運算子的優先等級在程式設計中的重要性,並通過範例展示了不同優先等級的運算順序對運算結果的影響。
Thumbnail
本章節介紹了程式語言中的各種運算符,包括算數運算子、比較運算子、賦值運算子、位元運算子,以及運算子的優先等級。每種運算子都有對應的範例程式碼和輸出結果,以幫助讀者更好地理解其用法和效果。此外,章節也強調了運算子的優先等級在程式設計中的重要性,並通過範例展示了不同優先等級的運算順序對運算結果的影響。
Thumbnail
這個章節主要介紹了Swift程式語言中物件導向程式設計的基本概念,包括類別、建構子、公開、私有、受保護等等的概念。同時,也介紹了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型和反射等進階特性。
Thumbnail
這個章節主要介紹了Swift程式語言中物件導向程式設計的基本概念,包括類別、建構子、公開、私有、受保護等等的概念。同時,也介紹了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型和反射等進階特性。
Thumbnail
本章節介紹了如何建立並設置Swift項目以及如何選擇和設置Swift代碼編輯器。這包括在Xcode和命令行中建立Swift項目,選擇Xcode、Visual Studio Code或AppCode作為編輯器,以及如何使用SPM安裝插件。
Thumbnail
本章節介紹了如何建立並設置Swift項目以及如何選擇和設置Swift代碼編輯器。這包括在Xcode和命令行中建立Swift項目,選擇Xcode、Visual Studio Code或AppCode作為編輯器,以及如何使用SPM安裝插件。
Thumbnail
5 月,方格創作島正式開島。這是一趟 28 天的創作旅程。活動期間,每週都會有新的任務地圖與陪跑計畫,從最簡單的帳號使用、沙龍建立,到帶著你從一句話、一張照片開始,一步一步找到屬於自己的創作節奏。不需要長篇大論,不需要完美的文筆,只需要帶上你今天的日常,就可以出發。征服創作島,抱回靈感與大獎!
Thumbnail
5 月,方格創作島正式開島。這是一趟 28 天的創作旅程。活動期間,每週都會有新的任務地圖與陪跑計畫,從最簡單的帳號使用、沙龍建立,到帶著你從一句話、一張照片開始,一步一步找到屬於自己的創作節奏。不需要長篇大論,不需要完美的文筆,只需要帶上你今天的日常,就可以出發。征服創作島,抱回靈感與大獎!
Thumbnail
此章節旨在介紹Swift程式語言中的基本資料型別,包括整數、浮點數、布林型別、字串、字符、選擇型、型別轉換、自訂型別(包括類、結構和枚舉)、元組型別、集合型別、陣列型別和字典型別。每種型別的說明都包含了程式碼範例,以便讀者更好地理解其應用和使用方式。
Thumbnail
此章節旨在介紹Swift程式語言中的基本資料型別,包括整數、浮點數、布林型別、字串、字符、選擇型、型別轉換、自訂型別(包括類、結構和枚舉)、元組型別、集合型別、陣列型別和字典型別。每種型別的說明都包含了程式碼範例,以便讀者更好地理解其應用和使用方式。
Thumbnail
本章節為Swift程式語言的異常處理介紹,說明了為何需要進行異常處理以及如何進行異常處理。提供了使用do、try、catch和throw關鍵字進行異常處理的基本語法並展示了其在實際程式中的應用。同時也說明了Swift中的一些常見異常類型,並且教導了如何主動觸發異常訊息和定義自己的異常類型。
Thumbnail
本章節為Swift程式語言的異常處理介紹,說明了為何需要進行異常處理以及如何進行異常處理。提供了使用do、try、catch和throw關鍵字進行異常處理的基本語法並展示了其在實際程式中的應用。同時也說明了Swift中的一些常見異常類型,並且教導了如何主動觸發異常訊息和定義自己的異常類型。
Thumbnail
當時間變少之後,看戲反而變得更加重要——這是在成為母親之後,我第一次誠實地面對這一件事:我沒有那麼多的晚上,可以任性地留給自己了。看戲不再只是「今天有沒有空」,而是牽動整個週末的結構,誰應該照顧孩子,我該在什麼時間回到家,隔天還有沒有精神帶小孩⋯⋯於是,我不得不學會一件以前並不擅長的事:挑選。
Thumbnail
當時間變少之後,看戲反而變得更加重要——這是在成為母親之後,我第一次誠實地面對這一件事:我沒有那麼多的晚上,可以任性地留給自己了。看戲不再只是「今天有沒有空」,而是牽動整個週末的結構,誰應該照顧孩子,我該在什麼時間回到家,隔天還有沒有精神帶小孩⋯⋯於是,我不得不學會一件以前並不擅長的事:挑選。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News