SwiftyJSON 源碼學習

jopen 9年前發布 | 11K 次閱讀 Apple Swift開發 SwiftyJSON

原文 http://www.jianshu.com/p/a9bdbd1255b9

SwiftyJSON 源碼學習

SwiftyJSON 是一個很優秀 Swift 語言第三方庫。我們在之前的文章中對它有過介紹。相信大家對它也有了一些了解。提升開發功力最好的方式就是學習優秀的源代碼了,記得大神 TJ Holowaychuk 也這么說過。所以我們這次一起來學習一下 SwiftyJSON 的代碼。

SwiftyJSON 很適合我們做源碼研究。首先,它的代碼量很少,整個庫只有一個代碼文件。這樣我們就能很快的了解它的整體結構。

另外,雖然它的代碼量不大,但是卻很充分的用到了 Swift 的特性。通過研究它,能幫助我們著切實的了解 Swift 以及這些特性的應用場景。

開始之前

首先呢,在學習 SwiftyJSON 代碼之前,最好先了解一下怎么使用它。關于 SwiftyJSON 的使用,我們之前這篇文章中有討論,如果大家之前沒有使用過 SwiftyJSON 可以先參考這里: 使用 SwiftyJSON 處理 JSON 解析

踏上發現之旅

那么我們開始吧。

首先,我們打開 SwiftyJSON 的項目文件,就可以看到它的結構啦:

SwiftyJSON 源碼學習

非常簡單,只有兩個文件SwiftyJSON.h和SwiftyJSON.swift。

其中SwiftyJSON.h文件中,只包含了兩個定義 :

FOUNDATION_EXPORT double SwiftyJSONVersionNumber;

FOUNDATION_EXPORT const unsigned char SwiftyJSONVersionString[];</pre>

分別用作表示 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 }

}</pre>

確實,它是一個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 }</pre>

咱們一點點分析,先說一下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

SwiftyJSON 源碼學習
 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!