Swift 類型分為兩種:值類型與引用類型。
值類型:每個實例擁有獨一無二的數(shù)據(jù)拷貝,例如基礎(chǔ)數(shù)據(jù)類型(Int、Float、Double等)、String、集合類型(Dictionary、Set、Array)、元組、枚舉、結(jié)構(gòu)體等。
引用類型:每個實例共享唯一一份數(shù)據(jù)拷貝,一般以類(Class)形式出現(xiàn)。
區(qū)別
值類型與引用類型最基本的區(qū)別就是拷貝之后的結(jié)果,值類型被拷貝的時候,相當(dāng)于建立一個新的獨立的實例,這個實例擁有自己的獨有的數(shù)據(jù)且不受其他實例的數(shù)據(jù)變化影響。也可以說拷貝在賦值、初始化、傳遞參數(shù)等過程中的數(shù)據(jù)并為數(shù)據(jù)創(chuàng)建一個獨立的實例。
struct Person{
var name: String?
init(name:String?) {
self.name = name
}
}
var p1 = Person.init(name: "hehe")
var p2 = p1 //p2是p1的拷貝
p2.name = "haha" //修改p2的數(shù)據(jù),p1并不被受影響
print(p1.name!,p2.name!)
//打印:hehe haha
拷貝一個引用類型的時候,本質(zhì)上是創(chuàng)建一個共享的實例的副本,兩者共用一套數(shù)據(jù)。在拷貝后,兩個實例指向了同一份數(shù)據(jù),所以修改其一的數(shù)據(jù),另一個實例隨之改變。
class Person{
var name: String?
init(name:String?) {
self.name = name
}
}
var p1 = Person.init(name: "hehe")
var p2 = p1 //p2是p1的拷貝
p2.name = "haha" //修改p2的數(shù)據(jù),p1的數(shù)據(jù)隨之改變,p1/p2共用一套數(shù)據(jù)
print(p1.name!,p2.name!)
//打印:haha haha
可變在安全性中的角色
使用值類型可以不用擔(dān)心在某個地方對數(shù)據(jù)的修改而影響到其他地方的數(shù)據(jù)。在多線程環(huán)境中非常有用,因為不同的線程有可能會在不知情的情況下改變數(shù)據(jù)。發(fā)生這種Bug后,調(diào)試就非常困難。
因為值類型和引用類型的區(qū)別就在于當(dāng)你修改類型實例的數(shù)據(jù)時,它們對原始類型數(shù)據(jù)的處理方式不同。但是有一種情況,值類型和引用類型的處理方式卻又相似,那就是當(dāng)類型實例的數(shù)據(jù)為只讀的時候。在不存在修改的情況下,值類型和引用類型就沒什么區(qū)別了。
在Swift中可以通過定義不可改變的存儲屬性來創(chuàng)建一個不可變的類,避免暴露出的API被修改。許多普通的Cocoa框架里的類,如NSURL,都被設(shè)計成不可變的類。
類型選擇
創(chuàng)建一個新類型的時候,如何選擇值類型還是引用類型呢?在使用Cocoa框架時,很多API都是NSObject的子類,那么就必須要使用引用類型,也就是類class。其他情況下,參考如下:
使用值類型的情景:
?使用==運算符比較實例數(shù)據(jù)時。
?單獨復(fù)制一份實例數(shù)據(jù)時。
?在多線程環(huán)境下操作數(shù)據(jù)時。
使用引用類型(比如class)的情景:
?當(dāng)使用===運算符判斷兩個對象是否引用同一個實例時。
?需要創(chuàng)建一個共享的、可變的對象時。