KakaJSON 底層實(shí)現(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是一個(gè)字典,調(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)
    }

幾個(gè)代碼點(diǎn)需要著重分析理解下

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,提示錯(cuò)誤信息并return。

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

2)let model = _ptr()

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

3)for property in properties {}

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

a)、

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

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


b)、

// 將json值轉(zhuǎn)換為用戶自定義model里面實(shí)現(xiàn)kj_modelValue方法的值,如果用戶沒有實(shí)現(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是不相等的,比如用戶類型寫錯(cuò)了或者也沒有實(shí)現(xiàn)kj_modelValue方法,類型無法匹配
if Swift.type(of: newValue) == propertyType {
    // 會(huì)把指針model傳入會(huì)被轉(zhuǎn)換為UnsafeMutablePointer<T>類型進(jìn)行賦值操作
    property.set(newValue, for: model)
    continue
}

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

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


看完這里你是不是會(huì)有一個(gè)疑問,就是如果類型不匹配怎么辦,即應(yīng)該有類型失敗的一個(gè)判斷,比如我們定義的存儲(chǔ)屬性是String,但是json返回的是一個(gè)Array數(shù)組?看流程圖的最后一步,as?就是類型轉(zhuǎn)換的一個(gè)判斷,如果類型不匹配是不會(huì)通過指針寫入內(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)換過程,可實(shí)現(xiàn)這2個(gè)方法

至此整個(gè)JSON解析過程我們就分析完了

很多時(shí)候我們?cè)诳匆恍┑谌綆煸创a的時(shí)候有時(shí)候會(huì)云里霧里的,這個(gè)時(shí)候可再去看看作者在github上面介紹的這個(gè)庫的功能點(diǎn),你就能豁然開朗,因?yàn)槲覀兤綍r(shí)在項(xiàng)目中用的時(shí)候并不是所有功能點(diǎn)都能用到,看實(shí)際需求。

參考文獻(xiàn):

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容