我們在前面的章節介紹了 函式( Function ),他是可以執行特定任務的獨立區塊程式碼,而 Swift 更進階的提供了兩個型別的語法:類別 ( class ) 及 結構( struct ),這兩個型別語法可以讓我們將多個相關的值及函數儲存在內,以及更多的特性
我們會遇到一個問題:到底 struct 和 class 到底差在哪?以下我們對 struct 及 class 做一個比較:
struct 及 class 相同的地方:
・屬性 ( property ):用於儲存值
・方法 ( method ):用於提供功能
・下標 ( subscript):用於存取值
・建構器 ( initializer ):用於生成初始化值
・擴展 ( extension ):增加預設實作功能
・協定 ( protocol ):對某類別提供標準功能
class 還比 struct 多了以下功能:
・繼承 ( inherit ):class 可以繼承另一個 class 的內容
・解構器 ( deinitializer ):允許一個類別實體釋放其所被分配的資源
・型別轉換:允許在執行時檢查和轉換一個類別實體的型別
・參考計數:
定義一個 class 或 struck 要分別使用 class 或 struck 等關鍵字
class 類別名稱 {
類別內屬性、方法或其他可以定義在內的屬性
}
struck 結構名稱 {
結構內屬性、方法或其他可以定義在內的屬性
}
這裡有幾點注意一下:
・class 或 struct 內的常數或變數稱之為屬性 ( property )
・class 或 struct 內的 function,稱之為方法 ( method )
・每次定義 class 或 struct 時,實際上是定義了一個新的型別,所以習慣上會以 大駝峰式命名法 來為 class 或 struct 命名,以符合 Swift 型別的命名風格
上述程式碼中,我們先行定義了一個角色設定的結構,這個結構裡面包含了兩個屬性:hp 及 mp,分別表示了角色的血量及魔力
接著我們定義了一個類別,來描述角色的基本屬性。而這個類別包含了三個屬性,第一個為角色狀態,會被初始化一個結構的實體;第二個為攻擊速度;最後一個為角色暱稱。
定義一個類別或結構,就像是給一個固定的規格,以遊戲來說明的話,就是這個角色一開始有的血量、魔力、攻擊速度以及角色暱稱。而我們在使用時,就必須按這這個規格 ( 也就是類別或結構 )來創建這個角色,這個動作就叫實例 ( instance )
類別與結構都使用建構器來實例化,建構器最簡單的形式就是在類別或結構後面名稱後面加上 ( ),這種實例化的方法,其內部的屬性都會初始化成預設值
使用點語法可以取得實例後的屬性,規則就是在實例名稱後面加上一個點,然後接著屬性名稱:
我們也可以取得子屬性,例如在 testGameCreate 中的 start 屬性:
當然,我們也可以指派值給實例後屬性:
所有結構都有自動生成的成員逐一建構器,當要生成一個結構的實體時,用來初始化實體的屬性:
Swift 以記憶體配置不同來說,分為值型別 ( value type )及參考型別( referce type )。值型別會實際儲存值;參考型別則會將值儲存在記憶體所配置的位置裡。
值型別在被指派給一個變數、常數或在被傳遞給一個函式時,實際上的操作是拷貝。指派或是傳遞後,兩者值即各自獨立,不互相影響。
而在前面的語法中,已經大量的使用值型別。實際上,Swift 中的基本型別:整數、浮點數、字串、布林值、陣列及字典,都是值型別,而且背後都是以結構的形式實作。
結構和列舉也都是值型別,代表他們的實體及其內任何值型別的屬性,在指派或傳遞時被複製:
在 firstPlayer 被指派給 iAmFirstPlayer 時,其實是將 firstPlayer 內的值進行拷貝,接著將拷貝的資料儲存給新的實體 iAmFirstPlayer,兩者是完全獨立的實體,彼此不會互相影響。
參考型別( reference type )在被指派給一個變數、常數或被傳遞給一個函式時,操作的不是其拷貝,而是已存在的實體本身:
在這裡要注意,雖然 secondPlayer 及 evolution 都被宣告為常數,卻都可以改變其內的屬性 ( secondPlayer.attackSpeed 及 evolution.attackSpeed ),因為這兩個常數儲存的是 GameCreate 類別實體的參考,而不是儲存這個實體,所以實體的 attackSpeed 這兩個實體可以被修改,但不會修改到常數的值。
因為類別是參考型別,可能有多個常數和變數在後台同時參考某一個類別實體,所以 Swift 提供了兩個恆等運算子,能夠判斷兩個變數或常數是不是參考同一個類別實體:
class 和 struct 有許多相似的地方,他們都可以定義自定義數據類型,作為程式碼的建構區塊。總之,class 是用來傳遞的;而 struct 則是透過複製來傳遞,這說明了他們適用於不同任務,所以當我們考慮項目所需數據結構及功能時,就要確定每個數據結構定義是 class 還是 struct。
按照準則,當符合以下一項或是多項情況時可以考慮創建 struct:
Struct 的主要目的封裝一些相對簡單的數據值。
當賦值或傳遞 Struct 實例時,合理的期望封裝的值是被複製的,而不是引用的。
Struct 儲存的任何屬性都是他們自己的值類型,它們也將被複製而不是引用。
該 Struct 不需要繼承其他現有類型的屬性或行為。
適合使用 Struct 創建的例子如下:
幾何形狀的大小,可能封裝了一個 width 和 height 屬性,兩者類型都為 Double
一定範圍的路徑,可能封裝了一個 start 和 length 屬性,兩者類型都為 Int
三維坐標系的一個點,可能封裝了 x , y 和 z 三個屬性,類型都為 Double
在其他情況下,你應該定義一個 Class 並創建該 Class 的實例,透過引用來管理和傳遞。實際上,這也代表著大多數自定義的數據結構應該為 Class 而不是 Struct。