HandyJSON 的優勢
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式, 應用廣泛. 在 App 的使用過程中, 服務端給移動端發送的大部分都是 JSON 數據, 移動端需要解析數據才能做進一步的處理. 在解析JSON數據這一塊, 目前 Swift 中流行的框架基本上是 SwiftyJSON, ObjectMapper, JSONNeverDie, HandyJSON 這么幾種.
我們應該如何選擇呢?
首先我們應該先明白解析 JSON 的原理. 目前有兩個方向.
保持 JSON 語義, 直接解析 JSON.
SwiftyJSON 就是這樣的. 本質上仍然需要根據 JSON 結構去取值.
預定義 Model 類, 將 JSON 反序列化類的實例, 再使用這些實例.
這種方式和 OC 中的 MJExtension 的思路是一致的. 在 Swift 中, ObjectMapper, JSONNeverDie, 以及 HandyJSON 做的都是將 JSON 文本反序列化到 Model 類上.
第二種思路是我們熟悉和比較方便的. 和服務端定義好數據結構, 寫好 Model 就可以直接解析.
第二種思路有三種實現方式:
完全沿用 OC 中的方式, 讓 Model 類繼承自 NSObject, 通過 class_copyPropertyList 方法拿到 Model 的各種屬性, 從 JSON 中拿到對應的 value, 再通過 OC 中 利用runtime 機制 實現的 KVC 方法為屬性賦值. 如 JSONNeverDie.
支持純 Swift 類, 但要求開發者實現 mapping 函數, 使用重載的運算符進行賦值, 如 ObjectMapper.
獲取到 JSON 數據后, 直接在內存中為實例的屬性賦值. HandyJSON 就是這樣實現的.
第一種方式的缺點在于需要強制繼承 NSObject, 這不適用于 struct 定義的 Model. 因為 struct 創建的 Model 不能通過 KVC 為其賦值.
第二種方式的缺點在于自定義 mapping 函數, 維護比較困難.
第三種方式在使用上和 MJExtension 基本差不多, 比較方便. 是我們所推薦的.
HandyJSON 解析數據的原理.
如何在內存上為實例的屬性賦值呢?
為屬性賦值, 我們需要以下步驟:
獲取到屬性的名稱和類型.
找到實例在內存中的 headPointer, 通過屬性的類型計算內存中的偏移值, 確定屬性在內存中的位置.
在內存中為屬性賦值.
在 Swift 中實現反射機制的類是 Mirror, 通過 Mirror 類可以獲取到類的屬性, 但是不能為屬性賦值, 它是可讀的. 但是我們可以直接在內存中為實例賦值. 這是一種思路. 另外一種思路是不利用 Mirror, 直接在內存中獲取到屬性的名稱和類型, 這也是可以的. 目前 HandyJSON 就是利用的第二種方式.
1. 核心原理:繞過反射,直接操作內存
傳統的 JSON 庫(如 ObjectMapper
)依賴 Swift 的 Mirror
反射機制遍歷模型屬性,但反射存在性能瓶頸且無法直接修改屬性值。HandyJSON 通過以下方式實現高效解析:
a. 利用類型元數據(Type Metadata)
- Swift 編譯器會為每個類型生成元數據,包含屬性名稱、類型、內存偏移量等信息。
- HandyJSON 直接訪問這些元數據,獲取屬性的名稱和內存位置,無需通過反射。
- 例如,結構體的屬性在內存中是連續排列的,通過元數據可以計算出每個屬性的偏移量。
b. 內存拷貝與指針操作
-
通過
UnsafeMutablePointer
直接操作模型實例的內存。 -
將 JSON 值轉換為目標類型后,直接寫入對應的內存地址。
-
示例代碼邏輯:
swift
let offset = getPropertyOffset(from: metadata) // 獲取屬性偏移量 let pointer = instancePointer + offset // 計算屬性內存地址 let value = parseJSONValue(...) // 解析 JSON 值 pointer.storeBytes(of: value, as: type) // 直接寫入內存
2. 實現步驟詳解
a. 類型元數據解析
- 獲取類型信息:通過
type(of:)
或泛型類型參數獲取類型的元數據。 - 解析屬性列表:從元數據中提取屬性名稱、類型、是否為可選類型(
Optional
)等信息。 - 處理繼承和協議:遍歷類型的繼承鏈,確保父類屬性也能被正確映射。
b. JSON 到內存的映射
- 解析 JSON:將 JSON 數據轉換為字典(
[String: Any]
)。 - 匹配鍵與屬性:將 JSON 的鍵與模型屬性名匹配(支持自定義鍵名映射)。
- 類型轉換:將 JSON 值轉換為目標屬性類型(如 String 轉 Int、處理嵌套模型等)。
- 內存寫入:通過指針將轉換后的值寫入模型實例的內存。
c. 特殊類型處理
- 可選類型(Optional):根據 JSON 是否存在鍵值決定是否寫入
nil
。 - 枚舉(Enum):將 JSON 值映射到枚舉的
rawValue
或關聯值。 - 泛型類型:需要特殊處理元數據的動態解析。