Swift 類型系統核心區分為值類型與引用類型,理解兩者語義差異對於寫出正確、高效能、執行緒安全的程式碼至關重要。本文深入剖析複製語義、變異安全性、選擇準則與進階考量。
| 分類 | 關鍵字 | 複製語義 | 資料共享 | 典型代表 |
|---|---|---|---|---|
| 值類型 | struct, enum, tuple | 深層複製 (獨立副本) | 無 | Int, String, Array, Dictionary, Set, 自訂 Struct |
| 引用類型 | class | 淺層複製 (共享實例) | 是 | 自訂 Class, 閉包, 函式型別 |
struct Point { var x: Int; var y: Int }
var a = Point(x: 1, y: 2)
var b = a // 複製:建立獨立副本
b.x = 10
print(a.x, b.x) // 1 10 ← a 不受影響
class PointClass { var x: Int; var y: Int }
var a = PointClass(x: 1, y: 2)
var b = a // 複製:共享同一實例
b.x = 10
print(a.x, b.x) // 10 10 ← a 也被修改!
let 常數保證不可變。DispatchQueue, NSLock, actor Swift 5.5+)。let 且無修改方法,實務上可視為值語義,但語言不強制。✅ 符合以下條件優先用 Struct/Enum:
== 比較資料內容而非身分。✅ 符合以下條件用 Class:
=== 比較實例身分 (同一物件)。deinit 執行清理邏輯。Array、String、Dictionary、Set 皆為值類型,實作寫時複製:
var a = [1, 2, 3]
var b = a // O(1) 共享底層緩衝區
b.append(4) // 觸發複製,a 不受影響
print(a, b) // [1,2,3] [1,2,3,4]
此設計兼顧效能 (未修改時零複製) 與安全性 (修改時語義如值類型)。
若 Struct 過大導致複製開銷大,可用類別包裝:
final class Box { var value: LargeStruct }
var a = Box(value: large)
var b = a // 僅複製參考,極快
或使用 @frozen (庫演進模式) 固定記憶體佈局。
| 誤區 | 事實 |
|---|---|
| Class 傳遞是「傳址呼叫」 | Swift 只有「傳值呼叫」,Class 傳遞的是「參考的值」 |
| Struct 不能有可變方法 | 可用 mutating 關鍵字修飾方法 |
| Enum 不能有關聯值 | Enum 強大支援關聯值、原始值、遞迴 |
| 值類型一定比 Class 快 | 大型 Struct 複製開銷可能超過 Class 參考傳遞 |
Equatable/Hashable 協議實作。Copy-on-Write 行為,必要時用 Class 包裝。