Swift 中Class和Struct異同
Swift 中類和結(jié)構(gòu)體有很多共同點(diǎn)。共同處在于:
- 定義屬性用于存儲值
- 定義方法用于提供功能
- 定義下標(biāo)操作使得可以通過下標(biāo)語法來訪問實(shí)例所包含的值
- 定義構(gòu)造器用于生成初始化值
- 通過擴(kuò)展以增加默認(rèn)實(shí)現(xiàn)的功能
- 實(shí)現(xiàn)協(xié)議以提供某種標(biāo)準(zhǔn)功能
與結(jié)構(gòu)體相比,類還有如下的附加功能:
- 繼承允許一個(gè)類繼承另一個(gè)類的特征
- 類型轉(zhuǎn)換允許在運(yùn)行時(shí)檢查和解釋一個(gè)類實(shí)例的類型
- 析構(gòu)器允許一個(gè)類實(shí)例釋放任何其所被分配的資源
- 引用計(jì)數(shù)允許對一個(gè)類的多次引用
Tip: 類的對象是引用類型,而結(jié)構(gòu)體是值類型。所以類的賦值是傳遞引用,結(jié)構(gòu)體則是Copy傳值,不是使用引用計(jì)數(shù)。
類為支持的額外功能會增加其復(fù)雜性。一般,更傾向使用選擇結(jié)構(gòu)和枚舉,因?yàn)樗麄兏菀桌斫?而類,則當(dāng)再在合適和必要的時(shí)候使用。實(shí)際上,這意味著大多數(shù)的自定義數(shù)據(jù)類型定義為結(jié)構(gòu)和枚舉就可以了。
值類型、引用類型 最基本的定義:
值類型每個(gè)實(shí)例都擁有其數(shù)據(jù)的一份副本。當(dāng)被賦值給一個(gè)變量或常量,或傳遞給一個(gè)函數(shù)時(shí)候,它會建立一份新的副本。
引用類型所有實(shí)例共享一個(gè)數(shù)據(jù)副本。當(dāng)被賦值給一個(gè)變量或常量,或傳遞給一個(gè)函數(shù)時(shí)候,一個(gè)引用類型一旦被初始化,會返回一個(gè)指向已存在實(shí)例的引用。
| 比較項(xiàng) | struct | class |
|---|---|---|
| 類型 | 值類型 | 引用類型 |
| 屬性初始化 | 可用默認(rèn)構(gòu)造直接初始化 | 需要自己創(chuàng)建構(gòu)造方法 |
| 變量賦值 | 深拷貝 | 淺拷貝,增加原對象引用 |
| 方法中修改屬性 | 需要添加mutating | 不需要 |
| 繼承關(guān)系 | 不能繼承 | 可以繼承 |
| 內(nèi)存 | 棧上,自動內(nèi)存管理 | 堆上,手動內(nèi)存管理 |
| 速度 | 高效 | 相比效率低 |
| 線程安全 | 自動線程安全的 | 大多是非線程安全的 |
| 與oc混編 | 不支持,oc無法調(diào)struct | 支持混編 |
| 序列化 | 不支持,可用字節(jié)轉(zhuǎn)NSData | 支持序列化 |
Class優(yōu)勢 Struct
- 混合開發(fā)中,OC無法調(diào)用swift的struct,因?yàn)閛c調(diào)用swift代碼,對象必須繼承nsobject
- struct不能相互繼承,
- struct不能被序列化成NSdata對象,所以不能存入NSUserDefaults,所以需要數(shù)據(jù)序列化,儲存最好用class實(shí)現(xiàn)
struct的優(yōu)點(diǎn)
- 安全性:Struct是值類型傳遞,沒有引用計(jì)數(shù)
- 內(nèi)存:由于他沒有引用計(jì)數(shù),不會因?yàn)檠h(huán)引動導(dǎo)致內(nèi)存泄露
- 速度:Struct 值類型通常是以棧分配,不是堆,所以Struct比class快的多
- 拷貝:當(dāng)你拷貝一個(gè)對象時(shí)不需要知道是深拷貝,淺拷貝
- 線程安全:無論從那個(gè)線程訪問Struct,都簡單
總結(jié):
各有優(yōu)缺點(diǎn),如果模型較小,無需繼承和存儲到NSUserDefaults,或無需oc使用時(shí),可以用Struct。
struct可以保證代碼更加安全可靠,以及struct+protocol更加切合swift面向協(xié)議編程的初衷
二、struct并不是swift中唯一的值類型,class也不是唯一的引用類型。下面是一些例子:
| 值類型 | 引用類型 |
|---|---|
| Int | Function |
| Double | Closures |
| String | NSString |
| Array | NSArray |
| Dictionary | NSDictionary |
| Set | NSSet |
| Struct | Class |
| Enum | NS 繼承NSObject相關(guān)數(shù)據(jù)類型 |
| Tuple |
備注:Swift把一個(gè)引用類型看成一個(gè)類,這和Objective-C中很像。Objective-C中一切繼承于NSObject都被按照引用類型存儲
三、我們什么時(shí)候選擇值類型而不用引用類型?
以下時(shí)候使用值類型:
想要用==比較實(shí)例數(shù)據(jù)。一個(gè)雙等號(==)用于比較值。
你想復(fù)制來建立獨(dú)立數(shù)據(jù)。
數(shù)據(jù)要在多線程的代碼中使用,那么你就不用擔(dān)心數(shù)據(jù)會被其他線程改變。
以下時(shí)候使用引用類型(比如一個(gè)類):
想要用===比較實(shí)例一致性。===會檢查兩個(gè)對象是否完全一致,包括存儲數(shù)據(jù)的內(nèi)存地址。
你想要創(chuàng)建用于共享,可改變的數(shù)據(jù)。
引用類型和值類型在內(nèi)存中怎么存儲?
- 值類型-在棧內(nèi)存中存儲
- 引用類型-在托管堆內(nèi)存中存儲
棧與堆的不同!
像前面說的,引用類型實(shí)例存在堆中,值類型實(shí)例比如結(jié)構(gòu)存在于一個(gè)稱為棧的內(nèi)存區(qū)域中。如果值類型實(shí)例是一個(gè)類的一部分,值會和類一起存在堆中。
棧被用于靜態(tài)存儲分配,棧用于動態(tài)存儲分配,它們都存在計(jì)算機(jī)的RAM中。
棧被CPU緊密管理并優(yōu)化,當(dāng)一個(gè)函數(shù)創(chuàng)建一個(gè)變量,棧會存儲這個(gè)變量,并在函數(shù)退出時(shí)候被毀掉。被分配到棧的變量直接存儲在內(nèi)存上,訪問這段內(nèi)存非??臁.?dāng)一個(gè)函數(shù)或者方法調(diào)用另一個(gè)函數(shù),另一個(gè)函數(shù)再依次調(diào)用其他函數(shù)等等,直到最后一個(gè)函數(shù)返回它的值之前,其他所有函數(shù)都會保持暫停執(zhí)行。
棧總是按照LIFO順序保留,最新保留的區(qū)塊總是會下一個(gè)釋放。這使得跟蹤記錄棧非常簡單,釋放一個(gè)棧上的區(qū)塊不過是調(diào)整一個(gè)指針。因?yàn)闂7浅=M織有序,所以它快捷高效。
系統(tǒng)使用堆存儲被其他對象引用的數(shù)據(jù),堆是一大片內(nèi)存,系統(tǒng)可以從中請求并動態(tài)分配內(nèi)存區(qū)塊。堆并不會像棧一樣自動毀掉它的對象,需要外部工作來處理這些。在蘋果設(shè)備中ARC就做這個(gè)工作。引用數(shù)量會被ARC追蹤,當(dāng)它變?yōu)?時(shí)對象會被釋放。因此整個(gè)過程(分配,追蹤引用,釋放)會比棧要慢。所以值類型要快于引用類型。