發現意外之美 - SwiftyJSON 源碼學習 | 咖啡時間

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

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

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

開始之前

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

踏上發現之旅

那么我們開始吧。

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

非常簡單,只有兩個文件 SwiftyJSON.hSwiftyJSON.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)
}

這個方法接收的是一個字典類型的參數,這個字段的鍵和值分別是 StringJSON 類型。構造方法的實現中,對 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 數據中的 [...]{...},并且 NSArrayNSDictionary 集合中存儲的值都是 JSON 原生值類型。

另外,從這兩個構造方法也可知 SwiftyJSON 的 JSON 對象的一些設計思路:

public init(_ jsonArray:[JSON]) {  
  // some code
}

public init(_ jsonDictionary:[String: JSON]) {  
  // some code
}

這兩個構造方法接受的都是 JSON 類型的集合,但最終會將這些集合中的原始數據合并成一個新的集合,然后設置到新構造的 JSON 對象中。

分析了一下 SwiftyJSON 的代碼,是不是覺得有一些收獲呢?我們從中得到了一些設計思路吧。不過,本人水平有限,也許未能分析的面面俱到,就讓這篇文章成為一個開始,我們一起學習進步吧。

更多精彩內容可關注微信公眾號:
swift-cafe

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,776評論 18 139
  • 前言 多年以前自學Java,在本地做了一些筆記。最近幾年流行播客,一方面防止丟失,一方面可以幫助其他小伙伴...
    chaohx閱讀 1,033評論 0 3
  • 成甲聲音超級暖,本人又那么帥,是不是讀書可以讓人變得好看?不然身邊愛學習的一個個怎么都長成小仙女的樣子! ...
    離井的蛙閱讀 457評論 0 0
  • 姓名:庹亞軍 公司:寧波貞觀電器有限公司 組別:第235期 利他一組 【日精進打卡第 29天】 【知~學習】 《六...
    tyj小電工閱讀 136評論 0 0
  • 開陽,北斗第六星,又稱武曲,司掌財富、武勇,在“萬般皆下品惟有讀書高”的歷史中,將星和財帛都不屬于主流,因為“慈不...
    塵世知行者閱讀 340評論 0 0