Swift — 在 Structures 和 Classes 中進行選擇

決定如何存儲數據和模型行為,從 Structures 和 Classes 中選出更適合的架構。

Jeremy Xue
Jeremy Xue ‘s Blog
6 min readApr 30, 2019

--

Photo by Esther Jiao on Unsplash

# 概論

Structures 和 Classes 在你的應用中用於儲存數據以及建模是不錯的選擇,但是兩者因為的擁有相似的特性使得選擇變得困難。

考慮以下建議來幫助你選擇當你在應用中添加一個新的資料類型哪個選項是合理的:

  • 默認情況下採用 Structures
  • 當你需要 Objective-C 互相操作時,採用 Classes
  • 當你需要控制正在建模的數據的身份( identity )時,請使用 Classes
  • 使用 Structures 與協議透過共享實現來採取行為

# 默認情況下採用 Structures

使用 Structures 來表示常見的數據類型。Swift 中的 Structures 包括許多在其他語言中僅限於 Classes 的功能 — 它們可以包含儲存屬性,計算屬性和方法。此外,Swift 中的 Structures 可以採用協議來通過默認實現來獲得行為。Swift 標準庫和 Foundation 採用的 Structures 在一些我們經常使用的類型,例如:數字、字串、陣列和字典。

使用 Structures 可以更容易去推理部分的程式碼,而不需要去考慮應用程序的整體狀況。因為 Structures 是值類型( Value Type ),不同於 Classes。Structures 的局部改動對於應用程序中其他的地方是看不見的,除非你有意將這些更改作為應用程序流程的一部份傳達。

因此,你可以查看程式碼中的一部分,並且更加確信對該部分中的實例進行更動將會更加明確,而不是從相關的函數中調用隱藏。

#當需要 Objective-C 互相操作時,採用 Classes

如果使用需要處理數據的 Objective-C API,或是需要將數據模型整合到 Objective-C 框架中定義的現有 Class 階層中,則可能需要使用 Classes 和 Class 繼承你的數據模型。例如,許多 Objective-C 框架揭露了你希望為 subClass 的 Classes。

#需要控制身份時,採用 Classes

Swift 中的 Classes 具有內建的身份( Identity )概念,因為它們為引用類型( Reference Type ),這意味著當兩個不同的 Class 的實例中每個儲存屬性具有相同的值時,它們仍被身份運算符(===)視為不同。

這意味著當你在應用程序中共享 Class 實例時,你對於該實例做的更改在程式碼中的每個部分都是可見的,它保持著對實例的引用。當你需要實例具有此類身份時,請使用 Classes。常見的用途是文件處理、網路連線、共享硬體媒介。

舉例,如果你具有一個類型來表示本地資料庫的連線,管理訪問到資料庫的程式碼,從你的應用程式查看時需要完全控制資料庫的狀態。在這種情況下使用 Class 是適合的,但是務必限制你的應用程序的哪個部分可以訪問到共享的資料庫對象。

⚠️ 重要

小心處理這種身分。在整個應用程序中普遍共享 Classes 實例會使邏輯錯誤更容易發生。你可能沒有預料到中度更改共享實例的後果,所以正確編寫此類的程式碼需要更多的時間。

# 當你不需控制身份時,採用 Structures

使用 Structures 當你對包含有關你無法控制身份的實體信息進行數據建模時,使用 Structures。

舉個例子,在諮詢遠端資料庫的應用程序中,實例的標誌可能由外部實體完全擁有,並由其標識符傳送。如果應用程序的模型的一致性在伺服器上,你可以將記錄建模為具有標識符的 Structures。在下面的範例中,jsonResponse 包含來自伺服器的編碼 PenPalRecord 實例:

struct PenPalRecord {
let myID: Int
var myNickname: String
var recommendedPenPalID: Int
}
var myRecord = try JSONDecoder().decode(PenPalRecord.self, from: jsonResponse)

本地的變動對類似於 PenPalRecord 等模型類型很有用。例如:應用程序可能會推薦多個不同的筆友來響應用戶回饋。由於 PenPalRecord 結構不控制底層資料庫記錄的身份,因此對本地 PenPalRecord 實例所做的更改意外更改了資料庫中的值。

如果應用程序的另一部分更改了 myNickname 並提交這個更動請求回伺服器,則最近被拒絕的筆友推薦不會被更動錯誤接受。由於 myID 宣告為常數,因此沒有辦法在本地做更改。因此,對於資料庫的請求不會意外的有更改的錯誤紀錄。

# 使用結構和協議來建模繼承和共享行為

Structures 和 Classes 都支援一種繼承方式。Structures 和協議只能採用協議,無法從 Classes 中繼承。但是,你可以使用 Class 繼承構建繼承階層的種類也可以使用協議繼承和 Structures 來建模。

如果您從頭開始構建繼承關係,則首選協議繼承。 協議允許 Classes、Structures 和 Enumerations 加入繼承,而 Classes 繼承只與其他 Class 兼容。 當您選擇如何建模數據時,首先嘗試使用協議繼承構建數據類型的層次結構,然後在 Structures 中採用這些協議。

# 後記

在編寫上一篇文章「 Swift 程式語言 — Structures & Classes 」時,偶然看到官方文件裡面多了這篇文章,想說來看看官方文件怎麼去考慮這兩者的選擇,順便翻譯整理成一篇文章。

實際上看起來依然會以 Structures 最為比較普遍的選擇,如果只是單純做個數據模型或是簡單儲存數據,沒有一些特別用途的話,使用 Structures 相對簡單又快速,也容易推斷。但是如果要做一些特殊用途需要 Classes 的特性時,那當然毫不猶豫地採用 Classes。

# 參考文件:

--

--

Jeremy Xue
Jeremy Xue ‘s Blog

Hi, I’m Jeremy. [好想工作室 — iOS Developer]