Swift 與 Objective-C

Swift 類別(class)與結構(struct)的差異總整理(值型別・參考型別)

使用 Swift 開發時,總會遇到至少一次讓你停下來思考的時刻。

閱讀 4 分鐘
Swift 類別(class)與結構(struct)的差異總整理(值型別・參考型別) 封面圖

使用 Swift 開發時,總會遇到至少一次讓你停下來思考的時刻。

「這個要做成 class,還是 struct?」

我剛開始學 Swift 時,也只是憑感覺選這兩者。直到遇到奇怪的錯誤,才真正研究它們的差異。

先說結論。

如果沒有特殊理由,請優先選擇結構(struct)。

只有在確實需要「共享的一份狀態」時,才使用類別。

Apple 的官方指南也是同樣的立場。今天就來逐一說明原因。


先看重點摘要(3 點)

先為忙碌的讀者整理重點。

  1. 結構是值型別。複製後會成為完全不同的值。
  2. 類別是參考型別。即使複製,仍會共同指向同一個實例。
  3. 如果搞不清楚,先用 struct,只有需要繼承或共享狀態時才用 class。

記住這三句,就完成一半了。


值型別與參考型別有什麼不同?

只用文字說明確實有點難懂。直接看程式碼最快。

先看結構。複製的瞬間,兩者就互不相干了。

struct Point { var x = 0 }

var a = Point()
var b = a      // 值已複製
b.x = 10

print(a.x, b.x)
// 輸出: 0 10

即使修改了ba仍然不變,因為兩者各自擁有自己的值。

接著看類別。程式碼看起來相同,但結果不同。

class Box { var x = 0 }

let a = Box()
let b = a      // 參考已複製(相同物件)
b.x = 10

print(a.x, b.x)
// 輸出: 10 10

只修改了b,結果a也變成 10 了。

因為兩者指向同一個實例。那個困擾我的錯誤就是這個。

複製還是共享?只要看箭頭就能理解。
複製還是共享?只要看箭頭就能理解。

那麼什麼時候該用哪一個?(選擇標準)

在實務上做判斷時,我會在腦中想起這張表。

情況 判斷
單純用來容納資料的模型 結構
希望複製後彼此保持獨立 結構
必須使用繼承 類別
在多個地方共享同一個實例 類別
需要與 Objective-C 互通 類別

再詳細整理一下,就是這樣。

  • 如果要表示座標、色彩、大小等值本身,使用結構很自然。
  • 像畫面控制器或網路管理器一樣,需要讓多個地方共同查看同一份狀態時,類別才是合適的選擇。
  • Apple 的 SwiftUI 也基於相同理由,將 View 定義為 struct,因為它輕量且容易預測。

順帶一提,Swift 標準函式庫中的ArrayStringDictionary全都是結構。這表示值型別就是基本選擇。

我總是先提出這個問題,再開始設計。
我總是先提出這個問題,再開始設計。

優先推薦結構的理由

值型別容易預測。

不用擔心自己傳出的值在其他地方被偷偷修改。即使傳給函式,原始值也很安全。

在多執行緒環境中也更放心。因為沒有共享狀態,資料競爭會減少。

相反地,類別雖然強大,卻需要小心使用。如果同一個實例被各處持有,就很難追蹤值究竟在哪裡改變。

因此養成「預設使用 struct,需要時才用 class」的習慣,能大幅減少錯誤。


何時使用,何時避免?

最後將判斷標準簡短整理如下。

  • 使用結構:以資料為中心的模型、需要獨立複製,以及重視執行緒安全時
  • 使用類別:需要繼承、必須共享單一實例,或識別(identity)具有意義時
  • 避免:習慣性地為簡單模型使用 class,或需要共享時硬要用 struct 迂迴處理

面試時會這樣問

Q. 結構與類別最大的差異是什麼?

結構是值型別,類別是參考型別。結構複製後會成為完全獨立的值,但類別即使複製仍指向同一個實例。此外,只有類別可以繼承並定義 deinit。

Q. Swift 為什麼建議使用結構?

因為值型別沒有共享狀態,副作用較少且容易預測。原始值不會在意外情況下被修改,在多執行緒環境中也安全,有助於減少錯誤。


一開始我也以為這只是語法差異,但越深入研究,越發現這其實是設計問題。

熟悉今天的判斷標準,從下一個專案開始就不會那麼猶豫。先問自己:是值,還是共享?

延伸閱讀