在通常的項目中,我們經(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版初學小項目
本文如果有什么錯誤或者您有更好的方法,歡迎指出