Swift中的值類型和參照類型

在Swift中,類型分爲兩類:第一種是值類型,該類型的每一個實例持有數據的副本,而且該副本對於每一個實例來講是獨一無二的一份,好比結構體(struct)、枚舉(enum)、元組(tuple)都是值類型。第二種是參照類型,該類型的實例共享數據惟一的一份副本(在native層面說的話,就是該類型的每一個實例都指向內存中的同一個地址),好比類(class)就是參照類型。在這篇文章中,咱們將深刻探討值類型和參照類型的使用價值,以及如何在某種場景下選擇正確的類型。swift

它們有什麼不一樣?

值類型最基本的特色就是複製,這影響到它的賦值、初始化、傳參等操做。來看看下面的代碼示例:安全

// 值類型示例
struct S { var data: Int = -1 }
var a = S()
var b = a                       // a複製一份,並將副本賦值給了b
a.data = 42                     // a的數據改變了,可是b的並無改變
println("\(a.data), \(b.data)") // prints "42, -1"

參照類型的複製行爲實際上是隱式的建立了一個共享的實例,做爲原始類型的參照。下面的例子中,兩個變量都會參照惟一的那個共享實例的數據,所當改變這兩個變量中任何一個的數據,都會一樣做用於原始類型的數據:多線程

// 參照類型示例
class C { var data: Int = -1 }
var x = C()
var y = x       // 將x賦值給y
x.data = 42     // 修改了x的數據,實際上是修改了參照數據,那麼y的數據也會改變 
println("\(x.data), \(y.data)") // prints "42, 42"

Mutation在安全性中的角色

選擇值類型的一個很重要的緣由是可讓你比較容易的理解和掌控你的代碼。若是你使用值類型,那麼都是惟一的數據值、類型的副本在和你打交道,你對數據的修改只做用於數據所屬的類型實例,因此你能夠不用擔憂由於你在某個地方對數據的修改而影響到其餘地方的數據。這在多線程環境中很是有用,由於在多線程下,不一樣的線程有可能會在你不知情的狀況下改變數據。發生這種Bug後,調試起來就很是困難。框架

由於值類型和參照類型的表象區別就在於當你修改類型實例的數據時,它們對原始類型數據的處理方式不一樣。可是有一種狀況,值類型和參照類型的處理方式卻又類似,那就是當類型實例的數據爲只讀的時候。在不存在修改的狀況下,值類型和參照類型就沒什麼區別了。線程

你可能會以爲這一點頗有用,假如說一個class是徹底不能被重定義的,那麼就比較符合使用Cocoa的NSObject對象的一些習慣,並能很好的保持本來的語義。今天,在Swift你能夠經過定義不可改變的存儲屬性來建立一個不可重定義的類,這樣能夠避免暴露出的API被修改。事實上,許多普通的Cocoa框架裏的類,好比NSURL,都被定義成了不可重定義的類。儘管如此,Swift目前還不提供任何機制像結構體(struct)和枚舉(enum)同樣去強制使一個class成爲不可重定義的類(好比像子類)。調試

如何選擇正確的類型?

若是你想建立一個新類型,那麼你應該選擇值類型仍是參照類型呢?當你使用Cocoa框架時,不少API都是NSObject的子類,那麼你就必需要使用參照類型,也就是class。在其餘狀況下,這裏有一些指導建議你能夠參考:code

使用值類型的情形:對象

  • 使用==運算符比較實例數據的時候。
  • 你想單獨複製一份實例數據的時候。
  • 當在多線程環境下操做數據的時候。

使用參照類型(好比class)的情形:內存

  • 當使用===運算符判斷兩個對象是否引用同一個對象實例的時候。
  • 當上下文須要建立一個共享的、可變的對象時。

在Swift中,ArrayStringDictionary都是值類型。它們的使用方式相似C語言中得int,每個實例都有一份數據。你不須要進行顯示的複製操做去防止數據在你不知情的狀況下被修改。更重要的是,你能夠跨線程進行傳參而不須要考慮同步的問題,由於傳遞值類型很安全。秉着高安全性的精神,這種類型劃分模式能幫助你在Swift中寫出更加有可預測性的代碼。get

本文首發地址:Swift中的值類型和參照類型

相關文章
相關標籤/搜索