討論Struct與Class之前,我們先來看一個概念:Value Type(值類型),Reference Type(引用類型):
1. 值類型的變量直接包含他們的數據,對於值類型都有他們自己的數據副本,因此對一個變量操作不可能影響另一個變量;
2. 引用類型的變量存儲對他們的數據引用,因此后者稱為對象,因此對一個變量操作可能影響另一個變量所引用的對象。
這就是我們之前博客中提到的深拷貝與淺拷貝,博客傳送《iOS 圖文並茂的帶你了解深拷貝與淺拷貝》,兩者的本質區別在於:深拷貝就是內容拷貝,淺拷貝就是指針拷貝
A. 是否開啟新的內存地址
B. 是否影響內存地址的引用計數
討論Struct與Class之前,我們先做好准備工作,首先分別創建一個Struct:【SNode】 與 Class:【CNode】
struct SNode { var Data: Int? }
class CNode { var Data: Int? }
下面我們通過代碼來理解兩者都有哪些異同:
1、property初始化的不同
let snode = SNode(Data: 4) // struct可直接在構造函數中初始化property print("snode.data:\(String(describing: snode.Data))")
let cnode = CNode() // class不可直接在構造函數中初始化property
cnode.Data = 5
print("cnode.data:\(String(describing: cnode.Data))")
打印結果:
snode.data:Optional(4) cnode.data:Optional(5)
主要的差別就是 class 在初始化時不能直接把 property 放在 默認的constructor 的參數里,而是需要自己創建一個帶參數的constructor, 如:
class CNode { var Data: Int? init(data: Int) { self.Data = data } }
let cnode = CNode.init(data: 5)
2、變量賦值方式不同(深淺copy)
// struct
let snode = SNode(Data: 4) var snode1 = snode snode1.Data = 5 print("snode.data:\(String(describing: snode.Data))\n snode1.data:\(String(describing: snode1.Data))")
打印結果:
snode.data:Optional(4) snode1.data:Optional(5)
說明:struct 賦值“=”的時候,會copy一份完整相同的內容給另一個變量 --> 【開辟了新的內存地址】
// class let cnode = CNode() cnode.Data = 5 let cnode1 = cnode cnode1.Data = 6 print("cnode.data:\(String(describing: cnode.Data))\n cnode1.data:\(String(describing: cnode.Data))")
打印結果:
cnode.data:6 cnode1.data:6
說明:class 賦值“=”的時候,不會copy一份完整的內容給另一個變量,只是增加了原變量內存地址的引用而已 --> 【沒有開辟了新的內存地址】
3、immutable 變量
Swift 語言的特色之一就是可變動內容和不可變內容用 var 和 let 來甄別,如果初始為let的變量再去修改會發生編譯錯誤。
struct也遵循這一特性
class不存在這樣的問題:從上面的賦值代碼能很清楚的看出來。
4、mutating function
struct SNode { var Data: Int } extension SNode { mutating func changeData(vaule: Int) { self.Data = vaule } }
class CNode { var Data: Int = 0 } extension CNode { func changeData(vaule: Int) { self.Data = vaule } }
說明:struct 和 class 的差別是 struct 的 function 要去改變 property 的值的時候要加上 mutating,而 class 不用。
5、繼承
struct不能繼承,class可以繼承。
6、struct比class更“輕量級”
struct分配在棧中,class分配在堆中。
題外話:Swift 把 Struct 作為數據模型的注意事項
優點:
1、安全性:
因為 Struct 是用值類型傳遞的,它們沒有引用計數。
2、內存:
由於他們沒有引用數,他們不會因為循環引用導致內存泄漏。
3、速度:
值類型通常來說是以棧的形式分配的,而不是用堆。因此他們比 Class 要快很多! (http://stackoverflow.com/a/24243626/596821)
4、拷貝:
Objective-C 里拷貝一個對象,你必須選用正確的拷貝類型(深拷貝、淺拷貝),而值類型的拷貝則非常輕松!
5、線程安全
值類型是自動線程安全的。無論你從哪個線程去訪問你的 Struct ,都非常簡單。
缺點:
1、Objective-C
當你的項目的代碼是 Swift 和 Objective-C 混合開發時,你會發現在 Objective-C 的代碼里無法調用 Swift 的 Struct。因為要在 Objective-C 里調用 Swift 代碼的話,對象需要繼承於 NSObject。
Struct 不是 Objective-C 的好朋友。
2、繼承
Struct 不能相互繼承。
3、NSUserDefaults
Struct 不能被序列化成 NSData 對象。
所以:如果模型較小,並且無需繼承、無需儲存到 NSUserDefault 或者無需 Objective-C 使用時,建議使用 Struct。
知識延伸:為什么訪問struct比class快?
“堆”和“棧”並不是數據結構上的Heap跟Stack,而是程序運行中的不同內存空間。棧是程序啟動的時候,系統事先分配的,使用過程中,系統不干預;堆是用的時候才向系統申請的,用完了需要交還,這個申請和交還的過程開銷相對就比較大了。
棧是編譯時分配空間,而堆是動態分配(運行時分配空間),所以棧的速度快。
從兩方面來考慮:
1.分配和釋放:堆在分配和釋放時都要調用函數(MALLOC,FREE),比如分配時會到堆空間去尋找足夠大小的空間(因為多次分配釋放后會造成空洞),這些都會花費一定的時間,而棧卻不需要這些。
2.訪問時間:訪問堆的一個具體單元,需要兩次訪問內存,第一次得取得指針,第二次才是真正得數據,而棧只需訪問一次。
