Swift裡面的類型分為兩種:
●實值型別(Value Types):每個執行個體都保留了一分專屬的資料拷貝,一般以結構體 (struct)、枚舉(enum) 或者元組(tuple)的形式出現。
●參考型別(Reference Type):每個執行個體共用同一份資料來源,一般以類(class)的形式出現。
在這篇博文裡面,我們會介紹兩種類型各自的優點,以及應該怎麼選擇使用。
實值型別與參考型別的區別
實值型別和參考型別最基本的分別在複製之後的結果。當一個實值型別被複製的時候,相當於創造了一個完全獨立的執行個體,這個執行個體保有屬於自己的專屬資料,資料不會受到其他執行個體的資料變化影響:
複製代碼 代碼如下:
// 下面是一個實值型別的例子
struct S { var data: Int = -1 }
var a = S()
var b = a // b是a的拷貝
a.data = 42 // 更改a的資料,b的不受影響
println("\(a.data), \(b.data)") // 輸出結果 "42, -1"
實值型別就好像身份證複印件一樣,複印出來之後,修改原件上面的內容,複印件上的內容不會變。
另一方面,複製一個參考型別的時候,實際上是默默地創造了一個共用的執行個體分身,兩者是共用一套資料。因此修改其中任何一個執行個體的資料,也會影響到另外那個。
複製代碼 代碼如下:
// 下面是一個參考型別的例子
class C { var data: Int = -1 }
var x = C()
var y = x // y是x的拷貝
x.data = 42 // 更改x的資料,等於同時修改了y
println("\(x.data), \(y.data)") // 輸出結果 "42, 42"
Mutation(修改)在安全中扮演的角色
實值型別較參考型別來說,會讓你更容易在大量代碼中理清狀況。如果你總是得到一個獨立的拷貝出來的執行個體,你就可以放心它不會被你app裡面的其他部分代碼默默地修改。這在多線程的環境裡面是尤為重要的,因為另外一個線程可能會在暗地裡修改你的資料。因此可能會造成嚴重的程式錯誤,這在調試過程中非常難以排除。
由於差別主要在於修改資料的後果,那麼當執行個體的資料唯讀,不存在需要更改的情況下,用哪種類型都是沒有分別的。
你可能在想,有的時候我可能也需要一個完全不變的類。這樣使用Cocoa NSObject對象的時候會比較容易,又可以保留值語義的好處。在今天,你可以通過只使用不可變的儲存屬性,和避開任何可以修改狀態的API,用Swift寫出一個不可變類(immutable class)。實際上,很多基本的Cocoa類,例如NSURL,都是設計成不可變類的。然而,Swift語言目前只強制struct和enum這種實值型別的不可變性,對類這種參考型別則沒有。(例如還不支援強制將子類的限制為不可變類)
如何選擇類型?
所以當我們想要建立一個新的類型的時候,怎麼決定用實值型別還是參考型別呢?當你使用Cocoa架構的時候,很多API都要通過NSObject的子類使用,所以這時候必須要用到參考型別class。在其他情況下,有下面幾個準則:
什麼時候該用實值型別:
●要用==運算子來比較執行個體的資料時
●你希望那個執行個體的拷貝能保持獨立的狀態時
●資料會被多個線程使用時
什麼時候該用參考型別(class):
●要用==運算子來比較執行個體身份的時候
●你希望有建立一個共用的、可變對象的時候
在Swift裡面,數組(Array)、字串(String)、字典(Dictionary)都屬於實值型別。它們就像C語言裡面簡單的int值,是一個個獨立的資料個體。你不需要花任何功夫來防範其他代碼在暗地裡修改它們。更重要的是,你可以線上程之間安全的傳遞變數,而不需要特地去同步。在Swift高安全性的精神下,這個模式會協助你用Swift寫出更可控的代碼。