SwiftyJSON 是一個很優秀 Swift 語言第三方庫。我們在之前的文章中對它有過介紹。相信大家對它也有了一些了解。提升開發功力最好的方式就是學習優秀的源代碼了,記得大神 TJ Holowaychuk 也這么說過。所以我們這次一起來學習一下 SwiftyJSON 的代碼。
SwiftyJSON 很適合我們做源碼研究。首先,它的代碼量很少,整個庫只有一個代碼文件。這樣我們就能很快的了解它的整體結構。
另外,雖然它的代碼量不大,但是卻很充分的用到了 Swift 的特性。通過研究它,能幫助我們著切實的了解 Swift 以及這些特性的應用場景。
開始之前
首先呢,在學習 SwiftyJSON 代碼之前,最好先了解一下怎么使用它。關于 SwiftyJSON 的使用,我們之前這篇文章中有討論,如果大家之前沒有使用過 SwiftyJSON 可以先參考這里:使用 SwiftyJSON 處理 JSON 解析
踏上發現之旅
那么我們開始吧。
首先,我們打開 SwiftyJSON 的項目文件,就可以看到它的結構啦:
非常簡單,只有兩個文件 SwiftyJSON.h
和 SwiftyJSON.swift
。
其中 SwiftyJSON.h
文件中,只包含了兩個定義 :
FOUNDATION_EXPORT double SwiftyJSONVersionNumber;
FOUNDATION_EXPORT const unsigned char SwiftyJSONVersionString[];
分別用作表示 SwiftyJSON 的版本號。主要的代碼,就都集中在 SwiftyJSON.swift
這個文件中了:
接下來,我們開始分析主體代碼吧,SwiftyJSON 中只有一個類 - JSON
,確切的說 JSON
不是一個 class
而是一個 struct
,看看它的定義就知道了:
public struct JSON {
// some code
public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) {
// some code
}
public init(_ object: AnyObject) {
// some code
}
public init(_ jsonArray:[JSON]) {
// some code
}
public init(_ jsonDictionary:[String: JSON]) {
// some code
}
}
確實,它是一個 struct
, 關于 struct
的特性,可以參看瞄神的這篇: 將 PROTOCOL 的方法聲明為 MUTATING
并且定義了 4 個構造方法,咱們來一一看一下這幾個構造方法的實現:
public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) {
do {
let object: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: opt)
self.init(object)
} catch let aError as NSError {
if error != nil {
error.memory = aError
}
self.init(NSNull())
}
}
第一個構造方法,允許我們用 NSData
來構建 JSON
對象,這個方法也是最常用到得。它接受 3 個參數。其中 data 參數是傳遞進來用于解析的 JSON 數據, options 是讀取選項,error 是錯誤指針。
其中 options 和 error 這兩個參數已經在定義的時候指定了默認值,所以我們調用的時候,只有 data 參數是必須傳遞進來的,其他兩個參數都是可選的。
我們再看這個構造方法內部的定義,一目了然,其實 SwiftJSON
內部還是用到了 NSJSONSerialization
來解析 JSON 數據。
解析完成之后,如果解析成功,就會調用另外一個構造方法 self.init(object)
來繼續初始化。
如果解析失敗,則調用 self.init(NSNull())
這個構造方法進行繼續的初始化。
再看下一個構造方法:
public init(_ object: AnyObject) {
self.object = object
}
這個方法接受的參數,也就是第一個構造方法中使用 NSJSONSerialization
解析后返回的 object 對象。處理也非常簡單,只是將解析后的 object 對象保存到這個類的屬性中。
接下來再繼續看:
public init(_ jsonArray:[JSON]) {
self.init(jsonArray.map { $0.object })
}
這個構造方法就比較有意思了,它接受的是一個 JSON
對象的數組,里面用了一個 map
方法 jsonArray.map { $0.object }
。 這個方法做的事情其實就是將 JSON 對象,的數組轉換成一般對象的數組。
還記得上一個構造方法的 self.object 嗎,這里面存放的其實就是 NSJSONSerialization
解析后的 JSON 數據。注意看 map 方法里面的實現:
jsonArray.map { $0.object }
其實這個方法,用一個更加詳細的寫法,就等同于這個:
jsonArray.map { element in
return element.object
}
咱們一點點分析,先說一下 map
方法,其實這個方法就是對數組中得元素進行一次變換處理,將每一個數組元素都替換成 map
調用的閉包中返回的值。
我們這個例子中的閉包,返回的就是 return element.object
這個值。也就是說,這個方法其實是把原本還有 JSON
對象類型的數組里面的所有元素,都替換成了,JSON
里面包含的 object 的值。
這樣經過這個 map
操作, jsonArray
這個數組的類型已經發生變化了。
而 SwiftyJSON 使用的這種方式 - jsonArray.map { $0.object }
是一種簡寫形式,$0
代表的就是遍歷的每一個元素,相當于 element
,而閉包有一個特性,如果沒有顯示的制定 return
語句,那么就將最后一個表達式執行的結果作為返回值。
恩,神奇的 Swift~
map
調用結束后,我們又將它傳入了另外一個構造方法:
self.init(jsonArray.map { $0.object })
因為 jsonArray 是一個 Array, Array 在 JSON 類所定義的 4 個構造方法中,能匹配這個參數的方法就只有這個了:
public init(_ object: AnyObject) {
self.object = object
}
哈哈,最后又回到咱們上一個討論的方法啦,將 jsonArray.map 調用后的返回值,賦值給 object 屬性。
其實就是相當于,將一個 JSON
類型的數組,轉換成普通 JSON 對象(注意:這兩個 JSON 的含義不同,前面那個 JSON 表示的是 SwiftyJSON 中所定義的 JSON 對象,而后面那個 JSON,表示的是 JSON 數據的意思。謹記謹記~)。
那么咱們繼續,來看看最后一個構造方法:
public init(_ jsonDictionary:[String: JSON]) {
var dictionary = [String: AnyObject]()
for (key, json) in jsonDictionary {
dictionary[key] = json.object
}
self.init(dictionary)
}
這個方法接收的是一個字典類型的參數,這個字段的鍵和值分別是 String
和 JSON
類型。構造方法的實現中,對 jsonDictionary
這個字典進行遍歷,然后將 JSON
類的 object 屬性,以相同的 key
值生成了一個新的字典,然后再次用這個新的字典調用構造方法:self.init(dictionary)
相信聰明的各位同學,已經看出來了。這個構造方法和之前那個調用 jsonArray.map { $0.object }
的構造方法如出一轍。
只不過這個是將字典中的 JSON
對象,做了一次拆包。將包含 JSON
對象類型的字典,轉換成包含普通對象的字典。
最后,self.init(dictionary)
依然調用的是那個接受 AnyObject
的構造方法。又構建了一個將 jsonDictionary
轉換后的普通字典作為 object 屬性值得 JSON 對象。
回顧總結
讀一讀源碼,是不是會有不少發現呢,我們看完 JSON
類的這幾個構造方法,其實就已經基本摸清 SwiftyJSON 的大致架構了。
從第一個構造方法中的解析方式得知,SwiftyJSON 的內部依然依賴于 NSJSONSerialization
機制來解析 JSON 數據。
它的內部,使用 object
屬性,來維護 NSJSONSerialization
所解析出來的原始內容。
NSJSONSerialization
解析出來的內容,一般是NSArray
或者NSDictionary
類型,分別對應 JSON 數據中的[...]
和{...}
,并且NSArray
和NSDictionary
集合中存儲的值都是 JSON 原生值類型。
另外,從這兩個構造方法也可知 SwiftyJSON 的 JSON 對象的一些設計思路:
public init(_ jsonArray:[JSON]) {
// some code
}
public init(_ jsonDictionary:[String: JSON]) {
// some code
}
這兩個構造方法接受的都是 JSON
類型的集合,但最終會將這些集合中的原始數據合并成一個新的集合,然后設置到新構造的 JSON 對象中。
分析了一下 SwiftyJSON 的代碼,是不是覺得有一些收獲呢?我們從中得到了一些設計思路吧。不過,本人水平有限,也許未能分析的面面俱到,就讓這篇文章成為一個開始,我們一起學習進步吧。
更多精彩內容可關注微信公眾號:
swift-cafe