在前面課程(1-8a, 3-10a)曾提過,Swift 是個「物件導向」 — 也就是以物件為核心的程式語言,Swift 幾乎所有資料類型都是物件,這也是現代程式語言的特徵之一。

在 Swift 中定義一個物件類型主要有兩種方法:struct 與 class,兩者功能非常接近,語法也幾乎一樣。若直接以程式碼來比較,假設我們要寫個朋友通訊錄,可以分別用 struct 與 class 定義兩種「朋友」物件類型如下:

// class v.s. struct
// Tested by Heman, 2023/07/04
// 第一段
struct 新朋友 {
    var 姓名: String
    var 手機號碼: String
}

class 舊朋友 {
    var 姓名: String
    var 手機號碼: String
    init(name: String, phone: String) {
        姓名 = name
        手機號碼 = phone
    }
}

let 小明 = 新朋友(姓名: "王小明", 手機號碼: "0919123456")
let 阿呆 = 舊朋友(name: "郝呆", phone: "0921543876")
print("1. \\(小明.姓名) \\(小明.手機號碼)")
print("2. \\(阿呆.姓名) \\(阿呆.手機號碼)")

以上用 struct 與 class 定義「新朋友」與「舊朋友」資料類型,屬性同樣都有姓名與手機號碼。

用 class 定義物件時,若屬性沒有給預設值,就一定要寫初始化函式 init(),否則會出現錯誤訊息。用 struct 方便一些,但並非不需要初始化函式,而是系統會自動幫我們產生預設的初始化函式。

初始化函式主要目的就是給每個屬性初始值,有了初始化函式,才能產出物件實例,如後面兩行 let 指定句,產出小明與阿呆兩個物件實例。最後再用 print() 列印物件屬性,用法完全相同,第一段執行結果如下:

  1. 王小明 0919123456
  1. 郝呆 0921543876

除了初始化函式有點小差異之外,class 與 struct 還有三個主要不同,第一是物件實例產生副本(如複製或當作參數)時,class 物件是 “Reference type”,而 struct 則是 “Value type”,下面會進一步說明。

第二是 class 物件類型之間,可以有繼承關係,下層(子)類型可以繼承上層(父)類型的所有屬性與方法,就像生物分類階層一樣(如人種具備靈長目的所有特徵,靈長目具備哺乳綱的所有特徵)。而 struct 物件類型之間,沒有繼承關係,改用規範(protocol)定義上層的共同屬性。

不要小看以上兩個不同點,這牽涉到物件的根本性質,造成 class 與 struct 在程式設計上完全不同的思考方式,就像兩個不同文化的外國人。

因此,雖然 class 與 struct 能夠混用(也經常混用),但對初學者而言,應儘量以其中之一為主、另一種為輔的做法,這也是第三個不同的原因,SwiftUI 物件大多採用 struct 定義,而過去的 UIKit 與 AppKit 則以 class 為主。