1. 程式人生 > >Swift中與Objective-C的區別 -> 值型別和引用型別

Swift中與Objective-C的區別 -> 值型別和引用型別

在Swift中,型別分為兩類:第一種是值型別,該型別的每個例項持有資料的副本,並且該副本對於每個例項來說是獨一無二的一份,比如結構體(struct)、列舉(enum)、元組(tuple)都是值型別。第二種是引用型別,該型別的例項共享資料唯一的一份副本(在native層面說的話,就是該型別的每個例項都指向記憶體中的同一個地址),比如類(class)就是引用型別。在這篇文章中,我們將深入探討值型別和引用型別的使用價值,以及如何在某種場景下選擇正確的型別。

它們有什麼不同?

值型別最基本的特點就是複製,這影響到它的賦值、初始化、傳參等操作。來看看下面的程式碼示例:

// 值型別示例structS
{vardata:Int=-1}vara=S()varb=a// a複製一份,並將副本賦值給了ba.data=42// a的資料改變了,但是b的並沒有改變println("\(a.data), \(b.data)")// prints "42, -1"

引用型別的複製行為其實是隱式的建立了一個共享的例項,作為原始型別的引用。下面的例子中,兩個變數都會引用唯一的那個共享例項的資料,所當改變這兩個變數中任何一個的資料,都會同樣作用於原始型別的資料:

// 引用型別示例classC{vardata:Int=-1}varx=C()vary=x// 將x賦值給yx.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。在其他情況下,這裡有一些指導建議你可以參考:

使用值型別的情形:

  • 使用==運算子比較例項資料的時候。
  • 你想單獨複製一份例項資料的時候。
  • 當在多執行緒環境下操作資料的時候。

使用引用型別(比如class)的情形:

  • 當使用===運算子判斷兩個物件是否引用同一個物件例項的時候。
  • 當上下文需要建立一個共享的、可變的物件時。

在Swift中,ArrayStringDictionary都是值型別。它們的使用方式類似C語言中得int,每一個例項都有一份資料。你不需要進行顯示的複製操作去防止資料在你不知情的情況下被修改。更重要的是,你可以跨執行緒進行傳參而不需要考慮同步的問題,因為傳遞值型別很安全。秉著高安全性的精神,這種型別劃分模式能幫助你在Swift中寫出更加有可預測性的程式碼。