iOS 開發中總會用到各種 JSON 模型轉換庫,這篇文章將會對常見的幾個開源庫進行一下評測。評測的內容主要集中在性能、功能、容錯性這幾個方面。
Manually
手動進行 JSON/Model 轉換,不用任何開源庫,可以進行高效、自由的轉換,但手寫代碼非常繁瑣,而且容易出錯。
YYModel
我造的一個新輪子,比較輕量(算上 .h 只有 5 個文件),支持自動的 JSON/Model 轉換,支持定義映射過程。API 簡潔,功能也比較簡單。
FastEasyMapping
Yalantis 開發的一個 JSON 模型轉換庫,可以自定義詳細的 Model 映射過程,支持 CoreData。使用者較少。
JSONModel
一個 JSON 模型轉換庫,有著比較簡潔的接口。Model 需要繼承自 JSONModel。
Mantle
Github 官方團隊開發的 JSON 模型轉換庫,Model 需要繼承自 MTLModel。功能豐富,文檔完善,使用廣泛。
MJExtension
國內開發者"小碼哥"開發的 JSON 模型庫,號稱性能超過 JSONModel 和 Mantle,使用簡單無侵入。國內有大量使用者。
所有開源庫代碼更新至 2015-09-18,以 Release 方式編譯,運行在 iPhone 6 上,代碼見https://github.com/ibireme/YYModel/tree/master/Benchmark。
用例1:GithubUser
從 https://api.github.com/users/facebook 獲取的一條 User 數據,去除 NSDate 屬性。
該 JSON 有 30 行,大部分屬性是 string,少量是 number。這個用例主要是測試最基礎的 Model 相關操作。
每次測試執行 10000 次,統計耗時毫秒數。
用例2: WeiboStatus
從官方微博 App 抓取一條內容完整的微博數據,JSON 總共有 580 行(是的,一條微博需要這麼大數據量),包含大量嵌套對象、容器對象、類型轉換、日期解析等。這個用例主要是測試在復雜的情況下不同庫的性能。
每次測試執行 1000 次,統計耗時毫秒數。
測試結果如下:
Mantle 在各個測試中,性能都是最差的
JSONModel 和 MJExtension 性能相差不多,但都比 Mantle 性能高。
FastEasyMapping 相對來說性能確實比較快。
YYModel 性能高出其他幾個庫一個數量級,接近手寫代碼的效率。
FastEasyMapping 不支持 NSCoding 協議,所以不能進行 Archive 的性能測試。
MJExtension 在處理復雜對象轉為 JSON 時,存在錯誤。
(此處我也測試了一些 Swift 的項目,例如 ObjectMapper、JSONHelper、ModelRocket,性能比 Mantle 還差很多,這裡就不進行對比了。)
容錯性主要是測試在默認情況下,當 JSON 格式錯誤時,Model 框架是否會產生錯誤結果或造成 Crash。
YYModel 和 Mantle 都會進行對象類型檢查,避免將錯誤的對象類型賦值到屬性,以避免潛在的 Crash 問題。不同的是 YYModel 會嘗試自動轉換,轉換失敗時留空;而 Mantle 遇到類型不匹配時,直接把錯誤向上返回,從而終止了整個轉換過程,但這麼做更方便調試。
MJExtension 會對部分對象進行自動轉換(比如 NSString 和 NSNumber 之間的轉換),但當自動轉換不能完成時,它會直接把 JSON 對象賦值給類型不匹配的 Model 屬性。這樣的結果會導致稍後 Model 在使用時,造成潛在的 Crash 風險。
JSONModel 並沒有對錯誤類型的檢測,並且沒有對 App 的保護,當出現異常時,會導致整個 App Crash,非常危險。
FastEasyMapping 表現則是最差的,它沒有自動轉換的機制,當遇到類型不匹配時,會導致錯誤的類型賦值,甚至直接 Crash。
就功能來說,Mantle 的可定制性最高,功能相對比較豐富。
YYModel、JSONModel、MJExtension 使用比較簡單,但功能相對 Mantle 稍弱。
FastEasyMapping 功能最少,使用也不算方便。
Mantle 和 JSONModel 都需要 Model 繼承自某個基類,靈活性稍差,但功能豐富。
YYModel、MJExtension 都是采用 Category 方式來實現功能,比較靈活,無侵入。
但注意 MJExtension 為 NSObject/NSString 添加了大量沒有前綴的方法,且方法命名比較通用,極易和一個工程內的其他類造成沖突。
FastEasyMapping 采用工具類來實現 Model 轉換的功能,最為靈活,但使用很不方便。
如果需要一個穩定、功能強大的 Model 框架,Mantle 是最佳選擇,它唯一的缺點就是性能比較差。
如果對功能要求並不多,但對性能有更高要求時,可以試試我的 YYModel。
JSONModel 由於沒有類型檢測,可能會產生潛在的問題,這裡不推薦使用。
MJExtension 也缺乏某些必要的類型檢測,同時會帶來大量 Category 命名污染,這裡也不推薦使用。
Swift 相關的幾個庫性能都比較差,所以非 Swift 項目不推薦使用。
1. 緩存
Model JSON 轉換過程中需要很多類的元數據,如果數據足夠小,則全部緩存到內存中。
2. 查表
當遇到多項選擇的條件時,要盡量使用查表法實現,比如 switch/case,C Array,如果查表條件是對象,則可以用 NSDictionary 來實現。
3. 避免 KVC
Key-Value Coding 使用起來非常方便,但性能上要差於直接調用 Getter/Setter,所以如果能避免 KVC 而用 Getter/Setter 代替,性能會有較大提升。
4. 避免 Getter/Setter 調用
如果能直接訪問 ivar,則盡量使用 ivar 而不要使用 Getter/Setter 這樣也能節省一部分開銷。
5. 避免多余的內存管理方法
在 ARC 條件下,默認聲明的對象是 __strong 類型的,賦值時有可能會產生 retain/release 調用,如果一個變量在其生命周期內不會被釋放,則使用 __unsafe_unretained 會節省很大的開銷。
訪問具有 __weak 屬性的變量時,實際上會調用 objc_loadWeak() 和 objc_storeWeak() 來完成,這也會帶來很大的開銷,所以要避免使用 __weak 屬性。
創建和使用對象時,要盡量避免對象進入 autoreleasepool,以避免額外的資源開銷。
6. 遍歷容器類時,選擇更高效的方法
相對於 Foundation 的方法來說,CoreFoundation 的方法有更高的性能,用 CFArrayApplyFunction() 和 CFDictionaryApplyFunction() 方法來遍歷容器類能帶來不少性能提升,但代碼寫起來會非常麻煩。
7. 盡量用純 C 函數、內聯函數
使用純 C 函數可以避免 ObjC 的消息發送帶來的開銷。如果 C 函數比較小,使用 inline 可以避免一部分壓棧彈棧等函數調用的開銷。
8. 減少遍歷的循環次數
在 JSON 和 Model 轉換前,Model 的屬性個數和 JSON 的屬性個數都是已知的,這時選擇數量較少的那一方進行遍歷,會節省很多時間。
原文地址http://blog.ibireme.com/2015/10/23/ios_model_framework_benchmark/