Swift 類別 (Classes) 教學

雖然 Swift 大力推崇 Struct,但 類別 (Class) 仍然有其無法取代的地位。在處理與 Objective-C 的相容性、需要繼承架構、或是需要共享單一實例狀態時,我們仍然必須使用 Class。

定義類別

使用 class 關鍵字定義。語法與 Struct 非常相似,但要注意 Class 沒有自動產生的 Memberwise Initializer,你通常需要自己寫 init

class VideoMode {
    var resolution = Resolution() // 假設 Resolution 是一個 Struct
    var interlaced = false
    var frameRate = 0.0
    var name: String?
    
    // Class 通常需要自行定義 init,除非屬性都有預設值
    init(name: String) {
        self.name = name
    }
}

參考型別 (Reference Types) 的意義

Class 是參考型別 (Reference Type)。這意味著變數儲存的不是物件本身,而是指向記憶體中那個物件的指標 (Pointer)

什麼是「參考型別」?

當你把一個 Class 實例賦值給另一個變數時,不會複製內容,而是共享同一個實例。

生活類比: 想像你和同事共用一份 Google Sheet (雲端試算表)。你只是把「連結 (URL)」傳給同事。當同事透過連結進去修改了資料,你螢幕上看到的內容也會跟著改變。因為你們看的是同一份文件。

let tenEighty = VideoMode(name: "1080p")
tenEighty.frameRate = 25.0

let alsoTenEighty = tenEighty // 這裡只是複製了「參考 (連結)」
alsoTenEighty.frameRate = 30.0 // 修改了這個參考指向的物件

print(tenEighty.frameRate) // 30.0 (原本的變數也被影響了!)

恆等運算子 (Identity Operators)

因為 Class 是共享的,有時候我們需要知道兩個變數是不是指向同一個實例。Swift 提供了 === (恆等) 和 !== (不恆等) 運算子。

if tenEighty === alsoTenEighty {
    print("這兩個變數指向同一個 VideoMode 實例")
}
注意:=== (是否指向同一記憶體位置) 和 == (內容是否相等) 是完全不同的概念。

Class 獨有的功能

以下功能是 Struct 做不到,專屬於 Class 的:

  1. 繼承 (Inheritance):一個類別可以繼承另一個類別,獲得其屬性與方法。(詳見 類別繼承)

  2. 型別轉換 (Type Casting):可以在執行時期檢查 (is) 或轉換 (as) 實例的型別。

  3. 解構器 (Deinitializers): Class 可以定義 deinit 方法。當一個實例被釋放 (記憶體回收) 前,deinit 會被自動呼叫。這通常用來釋放非 Swift 管理的資源 (如檔案開啟、Notification 移除)。

    class Player {
        deinit {
            print("Player 被釋放了,可以在這裡做清理工作")
        }
    }
    
  4. 參考計數 (Reference Counting): Swift 使用 ARC (Automatic Reference Counting) 來管理 Class 的記憶體。允許多個參考指向同一個實例。

總結:Struct vs Class 如何選擇?

這是一個經典的面試題,也是架構設計的關鍵。

特性Struct (結構)Class (類別)
型別語意Value Type (值型別)Reference Type (參考型別)
資料傳遞複製 (Copy)傳遞參考 (Share)
記憶體位置Stack (堆疊) - 較快Heap (堆積) - 較慢
繼承不支援支援
Mutating修改屬性需加 mutating不需要
主要用途資料模型 (Model), SwiftUI View共用狀態管理器 (ViewModel), 舊版 UIKit

黃金準則:預設使用 Struct。 只有當你需要「共享狀態」或「繼承」時,才使用 Class。