確定如何存儲數據和模型行爲。
總覽
結構體
和 類
是在應用程序中存儲數據和模型行爲的不錯選擇,但是 結構體
和 類
的相似性使其很難選擇。
在向你的應用程序添加數據類型時,可以參考以下建議:
- 默認情況下使用
結構體
。Default
- 需要
Objective-C
互操作性時,請使用類
。 - 需要控制數據模型的
同一性
時,請使用類
。 - 需要通過
協議繼承
共享行爲實現
時,請使用結構體
。
一、 默認選擇結構體
使用 結構體
表示常見數據類型。Swift
中的 結構體
包含許多功能,這些功能僅限於其他語言中的類:它們可以包含存儲屬性,計算屬性和方法。而且,Swift
結構體
可以通過遵守協議來獲得默認的實現行爲。Swift
標準庫和 Foundation
中對經常使用的數據類型都是用 結構體
實現的,例如數字,字符串,數組和字典。
使用 結構體
可以更輕鬆地推理代碼片段,而無需考慮應用程序的整體狀態。由於 結構體
是值類型(與 類
不同),因此對 結構體
的局部更改對應用程序的其餘部分不可見,除非您有意將這些更改作爲應用程序流程的一部分進行交流。結果,您可以查看一段代碼,並更有信心對該部分中的實例進行顯式更改,而不是通過與切向相關的函數調用進行不可見的更改。
二、需要與Objective-C互操作性時使用類
如果您使用需要處理數據的 Objective-C
API
,或者需要適應你的數據模型爲繼承或者定義在 Objective-C
框架中的類,則可能需要使用 類
和 類繼承
來對數據進行建模。例如,許多 Objective-C
框架都公開了您希望子類化的類。
三、需要控制同一性時使用類
Swift
中的類帶有內置的同一性
概念,因爲它們是 引用類型
。這意味着當兩個不同的類實例爲其每個存儲的屬性具有相同的值時,同一運算符(===
)仍將它們視爲不同。這也意味着,當您在應用程序中共享一個類實例時,對該實例所做的更改對於包含對該實例的引用的代碼的每一部分都是可見的。當您需要實例具有這種 同一性
時,請使用 類
。常見的用例是文件管理,網絡連接和共享的硬件中介,如CBCentralManager。
例如,如果您有一個表示本地數據庫連接的類型,則用於管理對該數據庫訪問權限的代碼需要完全控制從應用程序查看的數據庫狀態。在這種情況下,使用一個 類
是適當的,但是一定要限制應用程序的哪些部分可以訪問共享數據庫對象。
重要
小心對待同一性。在整個應用程序中普遍共享類實例使邏輯錯誤更有可能發生。您可能不會預期更改大量共享實例的後果,因此,正確編寫此類代碼需要做更多的工作。
四、當您不控制同一性時使用結構體
當你的數據模型包含的信息不需要 同一性
時,請使用 結構體
。
例如,在查詢遠程數據庫的應用中,實例的同一性可以完全由外部實體擁有,並可以通過標識符進行通信。如果應用程序模型的一致性存儲在服務器上,則可以將記錄建模爲帶有標識符的結構體。在下面的示例中,jsonResponse
包含來自服務器的編碼 PenPalRecord
實例:
struct PenPalRecord {
let myID: Int
var myNickname: String
var recommendedPenPalID: Int
}
var myRecord = try JSONDecoder().decode(PenPalRecord.self, from: jsonResponse)
像 PenPalRecord
模型類型的本地更改是非常有用的。例如,一個應用可能會響應用戶反饋而推薦多個不同的 penpals
。因爲該PenPalRecord
結構體無法控制基礎數據庫記錄的 同一性
,所以不存在對本地 PenPalRecord
實例所做的更改意外更改數據庫中值的風險。
如果應用程序的另一部分發生更改 myNickname
,並將更改請求提交給服務器,則更改不會錯誤地接受最近拒絕的penpals
建議。因爲該 myID
屬性被聲明爲常量,所以不能在本地更改。因此,對數據庫的請求不會意外更改錯誤的記錄。
五、使用結構體和協議建模來繼承和共享行爲
結構體
和 類
都支持一種 繼承
形式。結構體和協議只能採用協議;他們不能從類繼承。但是,還可以使用協議繼承和結構體對可以使用類繼承構建的繼承層次結構體進行建模。
如果要從頭開始建立繼承關係,則最好使用協議繼承。協議允許類,結構體和枚舉參與繼承,而類繼承僅與其他類兼容。當您選擇如何對數據建模時,請先嚐試使用協議繼承構建數據類型的層次結構體,然後在結構體中採用這些協議。
翻譯自官方Swift文檔:Choosing Between Structures and Classes