Swift 中的 struct 和 class

Swift 中 struct 是值類型,而 class 是引用類型,所以這篇文章 struct 的行為也可以用到所有的值類型上面,相同地 class 的行為也可以用到引用類型上。

相對于 Objective-C 中的結構體,Swift 對結構體的使用比重大了很多,結構體成為了實現面向對象的重要工具。Swift 中的結構體與 C++ 和 Objective-C 中的結構體有很大的差別,C++ 和 Objective-C 中的結構體只能定義一組相關的成員變量,而 Swift 中的結構體不僅可以定義成員變量(屬性),還可以定義成員方法。因此,我們可以把結構體看做是一種輕量級的類。

Swift 中類和結構體非常類似,都具有定義和使用屬性、方法、下標和構造器等面向對象特性,但是結構體不具有繼承性,也不具備運行時強制類型轉換、使用析構器和使用引用計等能力。

值類型的變量直接包含他們的數據,而引用類型的變量存儲對他們的數據引用,因此后者稱為對象,因此對一個變量操作可能影響另一個變量所引用的對象。對于值類型都有他們自己的數據副本,因此對一個變量操作不可能影響另一個變量。

1.類和結構體定義

Swift中的類和結構體定義的語法是非常相似的。我們可以使用 class 關鍵詞定義類,使用 struct 關鍵詞定義結構體,它們的語法格式如下:

// 定義類
class 類名 {
  定義類的成員
}
// 建立一個 class 名稱為 ClassCoder
class ClassCoder {
  var name = "IAMCJ"
  var age = 0
}

// 定義結構體
struct 結構體名 {
  定義結構體的成員
}
// 建立一個 struct 名稱為 StructCoder
struct StructCoder {
  var name = "IAMCJ"
  var age = 0
}

2.類和結構體實例化

// 類實例化
let classCoder = ClassCoder()
// class 不能直接用 ClassCoder(name:"CJ",age:18) 必需要自己創建構造函數才可以
classCoder.name = "CJ"
classCoder.age = 18

// 結構體實例化
var structCoder = StructCoder(name:"CJ",age:18)
// 另外一種實例化方法
var structCoder = StructCoder()
structCoder.name = "CJ"
structCoder.age = 18

區別:class 在實例化的時候不能自動把 property 放在 constructor 的參數里面去,想要和 struct 一樣的效果就需要我們自己去創建構造函數了。

3.賦值給另外一個變量

// 類賦值
let classCoder = ClassCoder()
classCoder.name = "CJ"
classCoder.age = 18
// classCoder.name=CJ,classCoder.age=18

let classCoder1 = classCoder
classCoder1.name = "NOTCJ"
classCoder1.age = 100
// classCoder.name=NOTCJ,classCoder.age=100,classCoder1.name=NOTCJ,classCoder1.age=100

// 結構體賦值
var structCoder = StructCoder()
structCoder.name = "CJ"
structCoder.age = 18
// structCoder.name=CJ,structCoder.age=18 

var structCoder1 = structCoder
structCoder1.name = "NOTCJ"
structCoder1.age = 100
// structCoder.name=CJ,structCoder.age=18,structCoder1.name=NOTCJ,structCoder1.age=100

區別:class 是引用類型,顧名思義在賦值的時候只是給另外一個變量賦予了一個引用的效果,而 struct 是值類型,它會復制一份完全相同的內容給另外一個變量,從上面的測試可以清晰的分辨出他們的不同之處。結合這篇文章可能能讓你更好的理解。

4.是否可變

let classCoder = ClassCoder()
classCoder.name = "CJ"
classCoder.age = 18
// 此處可以修改

let structCoder = StructCoder()
structCoder.name = "CJ"
// 此處會報錯

區別:let 在 class 上并不會報錯。但是 Swift 常用的 String, Array, Dictionary 都是 struct,所以 let 是會有效果的,這里需要大家注意一下。

5. mutating 關鍵字

//在不修改原 class 和 struct 的情況下添加一個 method:modifyCoderName(newName:)
// 類
class ClassCoder {
    var name = "IAMCJ"
    var age = 0
}
extension ClassCoder {
    func modifyCoderName(newName:String) {
        self.name = newName
    }
}

// 結構體
struct StructCoder {
    var name = "IAMCJ"
    var age = 0
}
extension StructCoder {
    mutating func modifyCoderName(newName:String) {
        self.name = newName
    }
}

區別:struct 在 function 里面需要修改 property 的時候需要加上 mutating 關鍵字,而 class 就不用了。

屏幕快照 2020-03-03 下午12.53.08.png

總結:struct與class 的區別

  1. struct是值類型,class是引用類型。
    值類型的變量直接包含它們的數據,對于值類型都有它們自己的數據副本,因此對一個變量操作不可能影響另一個變量。
    引用類型的變量存儲對他們的數據引用,因此后者稱為對象,因此對一個變量操作可能影響另一個變量所引用的對象。
    二者的本質區別:struct是深拷貝,拷貝的是內容;class是淺拷貝,拷貝的是指針。
  2. property的初始化不同:class 在初始化時不能直接把 property 放在 默認的constructor 的參數里,而是需要自己創建一個帶參數的constructor;而struct可以,把屬性放在默認的constructor 的參數里。
  3. 變量賦值方式不同:struct是值拷貝;class是引用拷貝。
  4. immutable變量:swift的可變內容和不可變內容用var和let來甄別,如果初始為let的變量再去修改會發生編譯錯誤。struct遵循這一特性;class不存在這樣的問題。
  5. mutating function: struct 和 class 的差別是 struct 的 function 要去改變 property 的值的時候要加上 mutating,而 class 不用。
  6. 繼承: struct不可以繼承,class可以繼承。
  7. struct比class更輕量:struct分配在棧中,class分配在堆中。

swift把struct作為數據模型的優缺點

優點

  1. 安全性: 因為 Struct 是用值類型傳遞的,它們沒有引用計數。
  2. 內存: 由于他們沒有引用數,他們不會因為循環引用導致內存泄漏。
  3. 速度: 值類型通常來說是以棧的形式分配的,而不是用堆。因此他們比 Class 要快很多!
  4. 拷貝:Objective-C 里拷貝一個對象,你必須選用正確的拷貝類型(深拷貝、淺拷貝),而值類型的拷貝則非常輕松!
  5. 線程安全: 值類型是自動線程安全的。無論你從哪個線程去訪問你的 Struct ,都非常簡單。

缺點

  1. Objective-C與swift混合開發:OC調用的swift代碼必須繼承于NSObject。
  2. 繼承:struct不能相互繼承。
  3. NSUserDefaults:Struct 不能被序列化成 NSData 對象。

所以:如果模型較小,并且無需繼承、無需儲存到 NSUserDefault 或者無需 Objective-C 使用時,建議使用 Struct。

class有這幾個功能struct沒有的:

  1. class可以繼承,這樣子類可以使用父類的特性和方法

  2. 類型轉換可以在runtime的時候檢查和解釋一個實例的類型

  3. 可以用deinit來釋放資源

  4. 一個類可以被多次引用
    struct也有這樣幾個優勢:

  5. 結構較小,適用于復制操作,相比于一個class的實例被多次引用更加安全。

  6. 無須擔心內存memory leak或者多線程沖突問題

順便提一下,array在swift中是用struct實現的。Apple重寫過一次array,然后復制就是深度拷貝了。猜測復制是類似參照那樣,通過棧上指向堆上位置的指針來實現的。而對于它的復制操作,也是在相對空間較為寬裕的堆上來完成的,所以性能上還是不錯的。

下面引用貓神OneV的博客:

var arr = [0,0,0]
var newArr = arr
arr[0] = 1
//Check arr and newArr
arr //[1, 0, 0]
newArr // before beta3:[1, 0, 0], after beta3:[0, 0, 0]

所以可以猜測其實在背后 Array和 Dictionary的行為并不是像其他 struct 那樣簡單的在棧上分配,而是類似參照那樣,通過棧上指向堆上位置的指針來實現的。而對于它的復制操作,也是在相對空間較為寬裕的堆上來完成的。當然,現在還無法(或者說很難)拿到最后的匯編碼,所以這只是一個猜測而已。

參考:http://www.lxweimin.com/p/596864f2c672
http://www.lxweimin.com/p/7622b4e81e59

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容