發現意外之美 - SwiftyJSON 源碼學習

發現意外之美 - SwiftyJSON 源碼學習

 

SwiftyJSON 是一個很優秀 Swift 語言第三方庫。咱們在以前的文章中對它有過介紹。相信你們對它也有了一些瞭解。提高開發功力最好的方式就是學習優秀的源代碼了,記得大神 TJ Holowaychuk 也這麼說過。因此咱們此次一塊兒來學習一下 SwiftyJSON 的代碼。json

SwiftyJSON 很適合咱們作源碼研究。首先,它的代碼量不多,整個庫只有一個代碼文件。這樣咱們就能很快的瞭解它的總體結構。swift

另外,雖然它的代碼量不大,可是卻很充分的用到了 Swift 的特性。經過研究它,能幫助咱們着切實的瞭解 Swift 以及這些特性的應用場景。數組

開始以前

首先呢,在學習 SwiftyJSON 代碼以前,最好先了解一下怎麼使用它。關於 SwiftyJSON 的使用,咱們以前這篇文章中有討論,若是你們以前沒有使用過 SwiftyJSON 能夠先參考這裏:使用 SwiftyJSON 處理 JSON 解析閉包

踏上發現之旅

那麼咱們開始吧。架構

首先,咱們打開 SwiftyJSON 的項目文件,就能夠看到它的結構啦:學習

很是簡單,只有兩個文件 SwiftyJSON.h 和 SwiftyJSON.swiftspa

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

FOUNDATION_EXPORT double SwiftyJSONVersionNumber;

FOUNDATION_EXPORT const unsigned char SwiftyJSONVersionString[];

分別用做表示 SwiftyJSON 的版本號。主要的代碼,就都集中在 SwiftyJSON.swift 這個文件中了:指針

接下來,咱們開始分析主體代碼吧,SwiftyJSON 中只有一個類 - JSON,確切的說 JSON不是一個 class 而是一個 struct,看看它的定義就知道了:code

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 的代碼,是否是以爲有一些收穫呢?咱們從中獲得了一些設計思路吧。不過,本人水平有限,也許未能分析的面面俱到,就讓這篇文章成爲一個開始,咱們一塊兒學習進步吧。

相關文章
相關標籤/搜索