Swift3.0 利用 Runtime 簡單封裝一個字典轉(zhuǎn)模型

在通常的項目中,我們經(jīng)常會用到字典轉(zhuǎn) model 的操作,我們可以使用系統(tǒng)的
setValuesForKeys(Swift)
setValuesForKeysWithDictionary(OC)
方法來完成這一操作,但是這樣就會遇到一個問題,如果我們數(shù)據(jù)字典其中的一個 key 與系統(tǒng)關鍵字重名,那我們在model中使用這個 key 作為屬性就會報錯,為了解決這一問題,我們會使用一些第三方庫去完成字典轉(zhuǎn)模型的操作,例如 MJExtension ,在這里,我們自己去封裝一個簡單的字典轉(zhuǎn)模型,閑話不多說,我們馬上開始。

首先我們?nèi)?chuàng)建一個 BaseModel 類,我們在這個根類中去實現(xiàn)一個可以字典轉(zhuǎn)自身屬性的構造方法,只要我們自定義的 model 都繼承這個 BaseModel 那么我們的 model 就都能使用這個構造方法完成字典轉(zhuǎn)模型的操作啦~

class BaseModel: NSObject { 
    //自定義構造方法
    init(dic: [String:Any]) {
        super.init()
    }
}

現(xiàn)在,我們已經(jīng)通過構造方法,拿到了數(shù)據(jù)字典,那么接下來我們只要將字典的鍵值對轉(zhuǎn)換為我們自身的屬性,就大功告成啦~
我們寫一個新的方法,去完成這個操作
我們首先在這個方法中使用 Runtime 獲取一下本類的所有屬性

func setAttribut(dic: [String:Any]) -> Void {
        //Runtime獲取本類屬性
        var count:UInt32 = 0
        let ivars = class_copyIvarList(self.classForCoder, &count)
}

然后我們遍歷這個獲取到的屬性數(shù)組,取出其中的元素,并獲得屬性名,這里值得注意的是,我們獲得的屬性名是 C 語言字符串,這里我們要轉(zhuǎn)換一下變成 Swift 字符串

for i in 0..<count {
         //取出屬性名
         let ivar = ivars?[Int(i)]
         let ivarName = ivar_getName(ivar!)
         let nName = String(cString: ivarName!)
}

進行到這一步,相信很多小伙伴已經(jīng)明白其中的原理了,接下來,我們只要利用取到的屬性名從我們的數(shù)據(jù)字典中取到相應的 value 然后賦值給我們的屬性,我們的任務就完成了,但是這里,我們要解決我們剛開始遇到問題 “我們的屬性名和字典的key值必須不相同怎么辦?” 在這里我的解決辦法是重新建立一個 model 屬性與字典 key 值的映射關系,這里又寫了一個建立映射的方法

//如果屬性名與數(shù)據(jù)字典的key值不對應,那么在子類model中復寫此方法,將屬性名作為key,字典key值作為value
    func attributesDic(dic: [String:Any]) -> [String:String] {
        var newDic:[String:String] = [:]
        for key in dic.keys {
            //復寫時注意將屬性名作為key 數(shù)據(jù)字典的key作為value
            newDic[key] = key
        }
        return newDic
    }

在這個 BaseModel 父類中,我們先讓數(shù)據(jù)字典所有的 key 映射為 key 本身,這樣我們在復寫這個方法時只修改 key 與屬性不對應的映射就可以了。
這里有特別注意的一點,在復寫時,我們一定要用 super 首先調(diào)用一下這個方法。

這樣,我們的屬性賦值方法就要修改了,我們要首先拿到數(shù)據(jù)字典的 key 與屬性的全新映射關系

func setAttribut(dic: [String:Any]) -> Void {
        //獲得映射關系
        let attributDic = attributesDic(dic: dic)        
        //Runtime獲取本類屬性
        var count:UInt32 = 0
        let ivars = class_copyIvarList(self.classForCoder, &count)
        for i in 0..<count {
            //取出屬性名
            let ivar = ivars?[Int(i)]
            let ivarName = ivar_getName(ivar!)
            let nName = String(cString: ivarName!)
        }  
}

這樣一來我們離成功就只差一步了!!
我們需要將取到的屬性名通過全新的映射關系取到數(shù)據(jù)字典的 key ,然后利用這個 key 從數(shù)據(jù)字典取到 value 最后將 value 賦值給我們 model 的屬性
最后,我們的屬性賦值方法變成了這樣

func setAttribut(dic: [String:Any]) -> Void {
        let attributDic = attributesDic(dic: dic)
        //Runtime獲取本類屬性
        var count:UInt32 = 0
        let ivars = class_copyIvarList(self.classForCoder, &count)
        for i in 0..<count {
            //取出屬性名
            let ivar = ivars?[Int(i)]
            let ivarName = ivar_getName(ivar!)
            let nName = String(cString: ivarName!)
            //取出要賦值的值
            var attribut = attributDic[nName]
            if attribut == nil{
                attribut = ""
            }
            var value:NSObject
            if dic[attribut!] != nil {
                value = dic[attribut!] as! NSObject
            } else {
                value = "" as NSObject
            }
            //利用KVC給本類的屬性賦值
            self.setValue(value, forKey: nName)   
        }        
}
最后的最后

在我們自定義的初始化方法中調(diào)用一下

//自定義構造方法
    init(dic: [String:Any]) {
        super.init()
        setAttribut(dic: dic)
    }

大功告成!!
這個封裝好的 model 已經(jīng)在我寫的 Swift 小項目中得到了驗證,這是項目地址
時光電影Swift版初學小項目
本文如果有什么錯誤或者您有更好的方法,歡迎指出

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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