開篇
Class和Struct是Swift中很重要的兩種數(shù)據(jù)結(jié)構(gòu),同時(shí)也是Swift面試題必問的一道題。所以對(duì)Class和Struct理解透徹對(duì)我們學(xué)習(xí)Swift有很大的幫助。
理解Class
Class的定義和使用
class Animal {
var name: String?
var weight = 0.0
}
let cat = Animal()
cat.name = "cat"
cat.weight = 10
print("cat's name: \(cat.name!), cat's weight: \(cat.weight)") //cat's name: cat, cat's weight: 10.0
Class是引用類型
當(dāng)值傳遞的時(shí)候,它是傳遞對(duì)已有instance的引用。下面用代碼來解釋一下這句話:
let cat = Animal()
cat.name = "cat"
cat.weight = 10
let blackCat = cat
blackCat.name = "blackCat"
print("cat's name: \(cat.name!)") // cat's name: blackCat
通過上面的代碼可以了解到,其實(shí)cat和blackCat指向的是同一個(gè)Animal的instance。它們只是這個(gè)instance的兩個(gè)不同的名字而已。如下圖所示:
Note
我們看到上述代碼把cat和blackCat聲明為了let,但是我們依然可以修改它的屬性,因?yàn)?code>cat和blackCat引用的instance并沒有改變,它們還是引用之前instance,我們只是修改的instance的屬性。但是不能將cat或者blackCat指向另一個(gè)實(shí)例,如blackCat = Animal()會(huì)提示Cannot assign to value: 'blackCat' is a 'let' constant的錯(cuò)誤。
Identity Operators
Swift 提供===和!==來判斷兩個(gè)變量或者常量是不是引用同一個(gè)instance(只用于class,想一想為什么struct不需要).
if blackCat === cat {
print("Identical") //Identical
} else {
print("Not identical")
}
===和==是不一樣的。
-
===:代表兩個(gè)變量或者常量引用的同一個(gè)instance -
==:代表兩個(gè)變量或者常量的值是否相同,并不一定是引用的同一個(gè)instance - 如果想讓自定義的class支持
==操作符,可以使該類遵守Equatable
class Animal {
var name: String?
var weight = 0.0
}
extension Animal: Equatable {
static func == (lhs: Animal, rhs: Animal) -> Bool {
return lhs.name == rhs.name && lhs.weight == rhs.weight
}
}
let cat = Animal()
cat.name = "cat"
cat.weight = 10
let blackCat = cat
blackCat.name = "catName"
let whiteCat = Animal()
whiteCat.name = "catName"
whiteCat.weight = 10.0
if blackCat === cat {
print("Identical") //Identical
} else {
print("Not identical")
}
if whiteCat === blackCat {
print("Identical")
} else {
print("Not identical") //Not identical
}
if whiteCat == blackCat {
print("Equal")
} else {
print("Not Equal") //Equal
}
理解Struct
Struct的定義和使用
struct FPoint {
var x = 0.0
var y = 0.0
//當(dāng)在struct修改屬性的時(shí)候需要使用mutating
mutating func addX(add: Double) {
self.x = self.x + add
}
}
let p1 = FPoint()
print("p1's x : \(p1.x), p1's y: \(p1.y)") // p1's x : 0.0, p1's y: 0.0
Struct是值類型
當(dāng)值進(jìn)行傳遞的時(shí)候,它會(huì)copy傳遞的值。下面用代碼來解釋一下這句話:
var p2 = p1
p2.x = 3.0
print("p1's x : \(p1.x), p1's y: \(p1.y); p2's x : \(p2.x), p2's y: \(p2.y)")
//p1's x : 1.0, p1's y: 2.0; p2's x : 3.0, p2's y: 2.0
通過上述代碼我們可以看到,將p1賦值給p2之后改變p2的值并不會(huì)影響p1的值,這是因?yàn)樵趯?code>p1賦值給p2的時(shí)候是拷貝一個(gè)instance值與p1相同,然后將拷貝的instance賦值給p2。如下圖所示:
Note
如果struct的instance聲明為let,是不能改變instance的值的。如
let p1 = FPoint(x: 1.0, y: 2.0)
p1.x = 10.0 //報(bào)錯(cuò):Cannot assign to property: 'p1' is a 'let' constant
在項(xiàng)目中如何選擇Struct和Class
- 默認(rèn)使用struct
- 當(dāng)你需要繼承Objective-C某些類的的時(shí)候使用class
- 當(dāng)你需要控制唯一性時(shí)使用class
- 使用struct和protocol來實(shí)現(xiàn)model繼承和共享行為,如下代碼所示:
protocol AnimalCommonProtocol {
var name: String? { get set }
var weight: Double { get set }
func run()
}
struct Cat : AnimalCommonProtocol {
func run() {
print("cat run")
}
var name: String?
var weight: Double
var gender: String?
}
struct Dog : AnimalCommonProtocol {
func run() {
print("dog run")
}
var name: String?
var weight: Double
var type: String?
}
總結(jié)起來就是一句話:能使用struct就不要使用class
為什么優(yōu)選struct
- 使用struct不需要考慮內(nèi)存泄漏和多線程讀寫的問題,因?yàn)樵趥鬟f值的時(shí)候它會(huì)進(jìn)行值的copy
- struct存儲(chǔ)在stack中,class存儲(chǔ)在heap中,struct更快
總結(jié)
相同點(diǎn)
- 都能定義property、method、initializers
- 都支持protocol、extension
不同點(diǎn)
- class是引用類型;struct是值類型
- class支持繼承;struct不支持繼承
- class聲明的方法修改屬性不需要
mutating關(guān)鍵字;struct需要 - class沒有提供默認(rèn)的memberwise initializer;struct提供默認(rèn)的memberwise initializer
- class支持引用計(jì)數(shù)(Reference counting);struct不支持
- class支持Type casting;struct不支持
- class支持Deinitializers;struct不支持