最近太忙太久沒寫文章了鞋囊,感覺有點(diǎn)不會寫了祖灰。
好了尿招,廢話不多說開始swift模型轉(zhuǎn)字典诀豁,字典轉(zhuǎn)模型的小小工具類編寫和思路窄刘。
思路:
以前 OC
是使用 runtime
獲取到 Mode
l 里面的 key
- value
然后用KVC
進(jìn)行賦值。
那么Swift
也可以使用 Mirror
來獲取 Model
里面的 key
- value
哦~舷胜,~娩践。
有了思路我現(xiàn)在就開始編寫,我在項(xiàng)目遇到的坑還有編寫遇到的坑烹骨,還有些沒解決的坑翻伺。??
以前 OC
中寫Model
會繼承 NSObject
,按我的習(xí)慣來沮焕,在Swift
我也習(xí)慣用NSObject
來寫Model
吨岭,所以現(xiàn)在我就用 NSObject
來寫擴(kuò)展(extension
)。
第一步峦树,使用Mirror
動(dòng)態(tài)獲取Model
的key
辣辫。
第二步,創(chuàng)建Model
魁巩,使用KVC
賦值急灭。
第三步,然后再轉(zhuǎn)Dictionary
谷遂。
基本步驟就這樣子把葬馋。
模型:
class Person : NSObject {
var name:String = ""
var age:Int = 0
var desc:String?
var height:Double?
}
把大部分可能性都寫了下,好測試埋凯。
有些坑都在?
上点楼,也就是Optional(可選值)
???♂?。
擴(kuò)展:
第一步:
先來點(diǎn)簡單的白对,使用Mirror
打印Model的 key
掠廓,type
extension NSObject {
func variables() {
let mirr = Mirror(reflecting: self)
//Mirror 的 children 是一個(gè) (label: String?, value: Any) 元組類型,表示該類的所有屬性的名字和類型
for case let (label,value) in mirr.children {
if let key = label {
let valueMirr = Mirror(reflecting: value)
//subjectType 類型
debugPrint("key -- \(key) type -- \(valueMirr.subjectType)")
}
}
}
}
終端打铀δ铡:
Test Case '-[ObjectConversionTests.ObjectConversionTests testModel]' started.
"key -- name type -- String"
"key -- age type -- Int"
"key -- desc type -- Optional<String>"
"key -- height type -- Optional<Double>"
然后就能看到寫 key
type
蟀瞧,這樣就好辦多了沉颂。知道key
就可以進(jìn)行KVC
了。
為了以后更可讀一點(diǎn)悦污,把一些常用基本類型進(jìn)行簡單的封裝成枚舉铸屉。
enum VariableType {
case number //數(shù)字
case string //字符串
case bool //布爾
case array(String) //數(shù)組 String 是對應(yīng)對象
case dictionary //字典
case object //對象
case null //NSNull
case unknown //無法解析數(shù)據(jù)
}
這一招我是模仿SwfitlyJson
在做的,可以在Github搜索到??切端。
我現(xiàn)在就將數(shù)據(jù)進(jìn)行分類組合~~
typealias VarData = (String,VariableType,String,Any.Type)// key type modelStr class 數(shù)據(jù)結(jié)構(gòu)
extension NSObject {
func variables() {
let mirr = Mirror(reflecting: self)
//Mirror 的 children 是一個(gè) (label: String?, value: Any) 元組類型彻坛,表示該類的所有屬性的名字和類型
var keys = [VarData]()
keys.append(contentsOf:self.categroy(mirr))
debugPrint(keys)
}
func categroy(_ mirr: Mirror) -> [VarData] {
var keys = [VarData]()
for case let (label,value) in mirr.children {
if let key = label {
let valueMirr = Mirror(reflecting: value)
var type = VariableType.unknown
var str = ""
if valueMirr.subjectType == String?.self || valueMirr.subjectType == String.self {
type = .string
} else if valueMirr.subjectType == Int?.self ||
valueMirr.subjectType == Int.self ||
valueMirr.subjectType == Int64?.self ||
valueMirr.subjectType == Int64.self ||
valueMirr.subjectType == Float?.self ||
valueMirr.subjectType == Float.self ||
valueMirr.subjectType == Double?.self ||
valueMirr.subjectType == Double.self {
type = .number
} else if valueMirr.subjectType == Bool?.self ||
valueMirr.subjectType == Bool.self {
type = .bool
} else {
let typestr = "\(valueMirr.subjectType)"
if typestr.contains("Array") {
str = typestr
str = str.replacingOccurrences(of: "Optional<", with: "")
str = str.replacingOccurrences(of: "Array<", with: "")
str = str.replacingOccurrences(of: ">", with: "")
type = .array(str)
} else if typestr.contains("Dictionary") {
type = .dictionary
} else {
if valueMirr.subjectType is NSObject.Type {
type = .object
} else if valueMirr.subjectType is NSObject?.Type {
type = .object
}
}
}
keys.append((key,type,str,valueMirr.subjectType))
}
}
return keys
}
}
Test Case '-[ObjectConversionTests.ObjectConversionTests testModel]' started.
[("name", ObjectConversion.VariableType.string, "", Swift.String),
("age", ObjectConversion.VariableType.number, "", Swift.Int),
("desc", ObjectConversion.VariableType.string, "", Swift.Optional<Swift.String>),
("height", ObjectConversion.VariableType.number, "", Swift.Optional<Swift.Double>)]
數(shù)據(jù)改造后就能清晰的看清楚數(shù)據(jù)對應(yīng)的類型是啥了,是不是瞬間感覺友善很多了哈踏枣。
你們應(yīng)該會有點(diǎn)疑問為啥那么多基礎(chǔ)類型都做雙判斷昌屉,這就是坑點(diǎn)。
if valueMirr.subjectType == Double.self {
...
}
起初我的判斷是這樣的以上判斷茵瀑。
但是他識別不了Optional
的變量间驮,所以我基本數(shù)據(jù)類型都會判斷Optional
。
if valueMirr.subjectType == Double.self
|| valueMirr.subjectType == Double?.self {
...
}
所以我才改成上面這樣子=马昨,=竞帽。
本來我是想過用字符串來進(jìn)行判斷的,后來想到這只是基本數(shù)據(jù)類型鸿捧,轉(zhuǎn)來轉(zhuǎn)去太麻煩了屹篓。
話題轉(zhuǎn)回去,接著繼續(xù)講轉(zhuǎn)換成模型的問題笛谦。
現(xiàn)在數(shù)據(jù)已經(jīng)有我需要的東東了抱虐。(key
type
)
現(xiàn)在做創(chuàng)建對象
和賦值的操作。
extension NSObject {
...
class func createObj(dict:Dictionary<String,Any>) -> Self {
let obj = self.init()//創(chuàng)建對象
//賦值操作
return obj
}
}
那么賦值操作步驟:
獲取key
type
關(guān)聯(lián)關(guān)系饥脑,上面我們就已經(jīng)獲取到了。
那么獲取后使用KVC進(jìn)行賦值懦冰。
extension NSObject {
...
func variables() -> [VarData] {
let mirr = Mirror(reflecting: self)
//Mirror 的 children 是一個(gè) (label: String?, value: Any) 元組類型灶轰,表示該類的所有屬性的名字和類型
var keys = [VarData]()
keys.append(contentsOf:self.categroy(mirr))
debugPrint(keys)
return keys
}
...
/**根據(jù)我的項(xiàng)目需求 服務(wù)器key 和模型key 大小寫不等。刷钢。笋颤。
* 此方法就過濾大小寫不等的問題。
*/
private func ignoreCase(dict:Dictionary<String,Any>,key:String) -> Any? {
if let value = dict[key] {
return value
} else if let value = dict[key.uppercased()] {
return value
} else if let value = dict[key.lowercased()] {
return value
} else {
let dks = dict.keys.filter({ (key2) -> Bool in
if key.uppercased() == key2.uppercased() {
return true
} else if key.lowercased() == key2.lowercased() {
return true
}
return false
})
if dks.count == 1 {
if let value = dict[dks.first!] {
return value
}
}
}
return nil
}
func pm_setValuesForKeys(_ dict:[String:Any]) {
let vars = self.variables()
for v in vars {
switch v.1 {
case .null:
break
case.unknown:
break
case .number:
if let value = ignoreCase(dict:dict,key:v.0) as? String {
let decimal = NSDecimalNumber(string: value)
if decimal != NSDecimalNumber.notANumber {
self.setValue(decimal, forKey: v.0)
}
} else {
if let value = ignoreCase(dict:dict,key:v.0) {
if !(value is NSNull) {
self.setValue(value, forKey: v.0)
}
}
}
case .string:
if let value = ignoreCase(dict:dict,key:v.0) as? NSNumber {
self.setValue(value.stringValue, forKey: v.0)
} else {
if let value = ignoreCase(dict:dict,key:v.0) as? String {
self.setValue(value, forKey: v.0)
} else {
if let value = ignoreCase(dict:dict,key:v.0) {
if !(value is NSNull) {
self.setValue(String(describing: ignoreCase(dict:dict,key:v.0)), forKey: v.0)
}
}
}
}
break
default:
if let value = ignoreCase(dict:dict,key:v.0) {
if !(value is NSNull) {
self.setValue(value, forKey: v.0)
}
}
}
}
}
class func createObj(dict:Dictionary<String,Any>) -> Self {
let obj = self.init() //創(chuàng)建對象
obj.pm_setValuesForKeys(dict) //賦值操作
return obj
}
}
那么我們字典轉(zhuǎn)換模型大概就這樣完成了内地。
但是呢~~運(yùn)行后會奔潰伴澄,這是為什么呢。
[ObjectConversionTests.ObjectConversionTests testModel] : failed: caught "NSUnknownKeyException", "[<ObjectConversion.Person 0x6000000a1920> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key height."
在Swift4.0這句話說找不到對應(yīng)key
阱缓。但是我利用Mirror
獲取到key
了呀非凌。。What荆针?
然后我發(fā)現(xiàn)Swift3.0 和 Swift4.0 模型的變量定義是不一樣的敞嗡。
Swift4.0是需要加@objc前綴修飾颁糟。
class Person : NSObject {
@objc var name:String = ""
@objc var age:Int = 0
@objc var desc:String?
@objc var height:Double = 0
}
然后在重新運(yùn)行,就運(yùn)行成功了喉悴。
在Swift3.0是不需要加的哦棱貌,具體為啥這里就不解釋了=,=箕肃。
做到這一步我們可以簡單的賦值了婚脱,但是還有Model
里面變量Model
的賦值還沒做。
下集分曉勺像。障贸。。剛好有活干了 T_T咏删。