KakaJSON 底層實現(xiàn)分析

1.代碼結(jié)構(gòu)

2.JSON解析出Model具體的流程

下面是官方demo的例子,我們拿此例子解析

import KakaJSON
struct Repo: Convertible {
      var name: String?
      var url: URL?
}
let json = [
            "name": "KakaJSON",
            "url": "https://github.com/kakaopensource/KakaJSON"
]
let repo = json.kj.model(Repo.self)
print(repo)
流程
1)調(diào)用json字典的kj屬性
public extension KJGenericCompatible {
    static var kj: KJGeneric<Self, T>.Type {
        get { return KJGeneric<Self, T>.self }
        set {}
    }
    var kj: KJGeneric<Self, T> {
        get { return KJGeneric(self) }
        set {}
    }
}
2)Base是一個字典阁簸,調(diào)用model方法
public extension KJGeneric where Base == [String: T] {
    /// JSONObject -> Model
    func model<M: Convertible>(_ type: M.Type) -> M {
        return model(type: type) as! M
    }
    
    /// JSONObject -> Model
    func model(type: Convertible.Type) -> Convertible {
        return base.kj_fastModel(type)
    }
}
3)調(diào)用Base的kj_fastModel方法
extension Dictionary where Key == String {
    func kj_fastModel(_ type: Convertible.Type) -> Convertible {
        var model: Convertible
        if let ns = type as? NSObject.Type {
            model = ns.newConvertible()
        } else {
            model = type.init()
        }
        model.kj_convert(from: self)
        return model
     }
........
}
4)賦值操作核心代碼
mutating func kj_convert(from json: [String: Any]) {
        guard let mt = Metadata.type(self) as? ModelType else {
            Logger.warnning("Not a class or struct instance.")
            return
        }
        guard let properties = mt.properties else {
            Logger.warnning("Don't have any property.")
            return
        }
        
        // get data address
        let model = _ptr()
        
        kj_willConvertToModel(from: json)
        
        // enumerate properties
        for property in properties {
            // key filter
            let key = mt.modelKey(from: property.name,
                                  kj_modelKey(from: property))
            
            // value filter
            guard let newValue = kj_modelValue(
                from: json.kj_value(for: key),
                property)~! else { continue }
            
            let propertyType = property.dataType
            // if they are the same type, set value directly
            if Swift.type(of: newValue) == propertyType {
                property.set(newValue, for: model)
                continue
            }
            
            // Model Type have priority
            // it can return subclass object to match superclass type
            if let modelType = kj_modelType(from: newValue, property),
                let value = _modelTypeValue(newValue, modelType, propertyType) {
                property.set(value, for: model)
                continue
            }
            
            // try to convert newValue to propertyType
            guard let value = Values.value(newValue,
                                           propertyType,
                                           property.get(from: model)) else {
                property.set(newValue, for: model)
                continue
            }
            
            property.set(value, for: model)
        }
        
        kj_didConvertToModel(from: json)
    }

幾個代碼點需要著重分析理解下

1)guard let mt = Metadata.type(self) as? ModelType

Metadata.type(self)是獲取當(dāng)前model的類型BaseType

首先我們看下各種Type的繼承關(guān)系圖:

我們定義的model要么是類class要么是結(jié)構(gòu)體struct肮帐,它們都繼承ModelType咖驮,所以這里判斷model的類型是ClassType或者StructType,如果定義的model不是這2種Type训枢,提示錯誤信息并return托修。

從Type繼承圖關(guān)系可以看到還有很多其它Type類型,如元組這種類型等恒界,主要應(yīng)用于model定義的存儲屬性睦刃,所以這里要區(qū)分開,不要搞混了十酣。

2)let model = _ptr()

這里的model是當(dāng)前定義的模型實例地址即指針涩拙,是UnsafeMutableRawPointer類型际长,后面在進(jìn)行set賦值的時候會通過運算符重載將其轉(zhuǎn)換為UnsafeMutablePointer<T>指針類型。

3)for property in properties {}

遍歷定義的存儲屬性兴泥,對每一個存儲屬性進(jìn)行賦值
具體分析下里面的賦值代碼:

a)工育、

// 這里是獲取key,返回的key是一個字符串或者數(shù)組
let key = mt.modelKey(from: property.name,kj_modelKey(from: property))

底層方法調(diào)用流程圖:


b)搓彻、

// 將json值轉(zhuǎn)換為用戶自定義model里面實現(xiàn)kj_modelValue方法的值如绸,如果用戶沒有實現(xiàn)kj_modelValue,那么直接返回jsonValue
guard let newValue = kj_modelValue(
                from: json.kj_value(for: key),
                property)~! else { continue }

底層方法調(diào)用流程圖:


c)旭贬、

// 如果它們類型相同直接賦值怔接,比如Any.Type和String.Type是不相等的,比如用戶類型寫錯了或者也沒有實現(xiàn)kj_modelValue方法稀轨,類型無法匹配
if Swift.type(of: newValue) == propertyType {
    // 會把指針model傳入會被轉(zhuǎn)換為UnsafeMutablePointer<T>類型進(jìn)行賦值操作
    property.set(newValue, for: model)
    continue
}

核心賦值操作都是調(diào)用property.set(newValue, for: model)扼脐,我們分析下底層這里是如何實現(xiàn)的:

為了能更直觀的展示調(diào)用過程,我們這里還是用流程圖:


看完這里你是不是會有一個疑問奋刽,就是如果類型不匹配怎么辦谎势,即應(yīng)該有類型失敗的一個判斷,比如我們定義的存儲屬性是String杨名,但是json返回的是一個Array數(shù)組脏榆?看流程圖的最后一步,as?就是類型轉(zhuǎn)換的一個判斷台谍,如果類型不匹配是不會通過指針寫入內(nèi)存的须喂。
d)、

// 如果上面都還沒有賦值成功趁蕊,那么就進(jìn)行類型轉(zhuǎn)換
guard let value = Values.value(newValue,
                               propertyType,
                               property.get(from: model)) else {
    // 類型轉(zhuǎn)換失敗坞生,用newValue直接進(jìn)行賦值,如果失敗交給as掷伙?去處理
    property.set(newValue, for: model)
    continue
}

e)是己、

kj_willConvertToModel(from: json)
kj_didConvertToModel(from: json)

當(dāng)有需要監(jiān)聽轉(zhuǎn)換過程,可實現(xiàn)這2個方法

至此整個JSON解析過程我們就分析完了

很多時候我們在看一些第三方庫源碼的時候有時候會云里霧里的任柜,這個時候可再去看看作者在github上面介紹的這個庫的功能點卒废,你就能豁然開朗,因為我們平時在項目中用的時候并不是所有功能點都能用到宙地,看實際需求摔认。

參考文獻(xiàn):

https://github.com/kakaopensource/KakaJSON
http://www.reibang.com/p/9ca7529306b2

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市宅粥,隨后出現(xiàn)的幾起案子参袱,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抹蚀,死亡現(xiàn)場離奇詭異剿牺,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)环壤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進(jìn)店門牢贸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人镐捧,你說我怎么就攤上這事〕粼觯” “怎么了懂酱?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長誊抛。 經(jīng)常有香客問我列牺,道長,這世上最難降的妖魔是什么拗窃? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任瞎领,我火速辦了婚禮,結(jié)果婚禮上随夸,老公的妹妹穿的比我還像新娘九默。我一直安慰自己,他們只是感情好宾毒,可當(dāng)我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布驼修。 她就那樣靜靜地躺著,像睡著了一般诈铛。 火紅的嫁衣襯著肌膚如雪乙各。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天幢竹,我揣著相機(jī)與錄音耳峦,去河邊找鬼。 笑死焕毫,一個胖子當(dāng)著我的面吹牛蹲坷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播邑飒,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼冠句,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了幸乒?” 一聲冷哼從身側(cè)響起懦底,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后聚唐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丐重,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年杆查,在試婚紗的時候發(fā)現(xiàn)自己被綠了扮惦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡亲桦,死狀恐怖崖蜜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情客峭,我是刑警寧澤豫领,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站舔琅,受9級特大地震影響等恐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜备蚓,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一课蔬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧郊尝,春花似錦二跋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至横缔,卻和暖如春铺遂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背茎刚。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工襟锐, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膛锭。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓粮坞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親初狰。 傳聞我的和親對象是個殘疾皇子莫杈,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,700評論 2 345

推薦閱讀更多精彩內(nèi)容