深入理解Swift中的Class和Struct

godiscoder發表於2019-02-13

開篇

Class和Struct是Swift中很重要的兩種資料結構,同時也是Swift面試題必問的一道題。所以對Class和Struct理解透徹對我們學習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是引用型別

當值傳遞的時候,它是傳遞對已有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
複製程式碼

通過上面的程式碼可以瞭解到,其實catblackCat指向的是同一個Animal的instance。它們只是這個instance的兩個不同的名字而已。如下圖所示:

深入理解Swift中的Class和Struct

Note

我們看到上述程式碼把catblackCat宣告為了let,但是我們依然可以修改它的屬性,因為catblackCat引用的instance並沒有改變,它們還是引用之前instance,我們只是修改的instance的屬性。但是不能將cat或者blackCat指向另一個例項,如blackCat = Animal()會提示Cannot assign to value: 'blackCat' is a 'let' constant的錯誤。

Identity Operators

Swift 提供===!==來判斷兩個變數或者常量是不是引用同一個instance(只用於class,想一想為什麼struct不需要).

if blackCat === cat {
    print("Identical") //Identical
} else {
    print("Not identical")
}
複製程式碼

=====是不一樣的。

  • ===:代表兩個變數或者常量引用的同一個instance
  • ==:代表兩個變數或者常量的值是否相同,並不一定是引用的同一個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
    //當在struct修改屬性的時候需要使用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是值型別

當值進行傳遞的時候,它會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的值並不會影響p1的值,這是因為在將p1賦值給p2的時候是拷貝一個instance值與p1相同,然後將拷貝的instance賦值給p2。如下圖所示:

深入理解Swift中的Class和Struct

Note

如果struct的instance宣告為let,是不能改變instance的值的。如

let p1 = FPoint(x: 1.0, y: 2.0)
p1.x = 10.0 //報錯:Cannot assign to property: 'p1' is a 'let' constant
複製程式碼

在專案中如何選擇Struct和Class

  • 預設使用struct
  • 當你需要繼承Objective-C某些類的的時候使用class
  • 當你需要控制唯一性時使用class
  • 使用struct和protocol來實現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?
}
複製程式碼

總結起來就是一句話:能使用struct就不要使用class

為什麼優選struct

  • 使用struct不需要考慮記憶體洩漏和多執行緒讀寫的問題,因為在傳遞值的時候它會進行值的copy
  • struct儲存在stack中,class儲存在heap中,struct更快

總結

相同點

  • 都能定義property、method、initializers
  • 都支援protocol、extension

不同點

  • class是引用型別;struct是值型別
  • class支援繼承;struct不支援繼承
  • class宣告的方法修改屬性不需要mutating關鍵字;struct需要
  • class沒有提供預設的memberwise initializer;struct提供預設的memberwise initializer
  • class支援引用計數(Reference counting);struct不支援
  • class支援Type casting;struct不支援
  • class支援Deinitializers;struct不支援

參考

相關文章