关键词
Swift 反射 Mirror
Representation of the sub-structure and optional "display style" of any arbitrary subject instance.
Describes the parts---such as stored properties,collection elements,tuple elements,or the active enumeration case---that make up a particular instance. May also supply a "display style" property that suggests how this structure might be rendered.
/// A collection of `Child` elements describing the structure of the
/// reflected subject.
public let children: Children
/// Suggests a display style for the reflected subject.
public let displayStyle: Mirror.displayStyle?
@warn_unused_result
public func superclassMirror() -> Mirror?
children记录了该object下的变量(注:此处object为广义的定义)
displayStyle表示该object的类型(同上)
superclassMirror,顾名思义,表示其父类的Mirror
public enum displayStyle {
case Struct
case Class
case Enum
case Tuple
case Optional
case Collection
case Dictionary
case Set
}
根据displayStyle的类型去判断类型再做分类讨论,其中Optional是Swift中独有比较特别的类型,需要特殊处理,当displayStyle为nil时,表示为基本数据类型
最后通过superclassMirror一层一层递归,去遍历所有变量
func getModel(dic:Dictionary<String,Any>,objName:String) -> MYDataModel {
let modelClass = NSClassFromString(objName) as! MYDataModel.Type
model = modelClass.init()
var mirror:Mirror? = Mirror(reflecting: model)
while (mirror != nil) {
traverseProperty(dic,mirror,model)
mirror = mirror!.superclassMirror()
}
return model
}
private func traverseProperty(dic:Dictionary<String,_ mirror:Mirror?,_ model:MYDataModel) {
if (mirror == nil) {
return
} else {
for (_,value) in mirror!.children.enumerate() {
let propertyName:String = value.label == nil ? "" : value.label!
let properValue = value.value
let dictionaryValue = dic[propertyName]
let tmp = Mirror(reflecting: properValue)
if (dictionaryValue != nil) {
if (tmp.displayStyle != nil) {
switch tmp.displayStyle! {
case .Class:
if let dicValue = dictionaryValue as? [String: String] {
let anyClass = model.classForCoder as? NSObject.Type
let arrayTypeName = anyClass!.performSelector(Selector("\(propertyName)Type")).takeRetainedValue() as? String
let anyArrayClass = NSClassFromString(arrayTypeName!) as! MYDataModel.Type
let obj = anyArrayClass.init()
traverseProperty(dicValue,Mirror(reflecting: obj),obj)
model.setValue(obj,forKey: propertyName)
}
case .Collection:
let anyClass = model.classForCoder as? NSObject.Type
let arrayTypeName = anyClass!.performSelector(Selector("\(propertyName)Type")).takeRetainedValue() as? String
switch arrayTypeName! {
case MYDataModelType.String.rawValue:
if let arrayValue = dictionaryValue as? [String] {
if (arrayValue.count > 0) {
model.setValue(arrayValue,forKey: propertyName)
}
}
default:
if let arrayValue = dictionaryValue as? [[String: String]] {
var resultArray = [(NSClassFromString(arrayTypeName!) as! MYDataModel.Type).init()]
for item in arrayValue {
let anyArrayClass = NSClassFromString(arrayTypeName!) as! MYDataModel.Type
let obj = anyArrayClass.init()
traverseProperty(item,obj)
resultArray.append(obj)
}
model.setValue(resultArray,forKey: propertyName)
}
break
}
case .Dictionary:
print("Dictionary")
case .Optional:
if (tmp.children.count > 0) {
for (_,value2) in tmp.children.enumerate() {
let properValue2 = value2.value
switch properValue2 {
case _ as Bool:
if let boolValue = dictionaryValue as? Bool {
model.setValue(NSNumber(bool:boolValue),forKey: propertyName)
}
case _ as Int:
if let intValue = dictionaryValue as? Int {
model.setValue(NSNumber(integer: intValue),forKey: propertyName)
}
case _ as Float :
if let floatValue = dictionaryValue as? Float {
model.setValue(NSNumber(float: floatValue),forKey: propertyName)
}
case _ as Double :
if let doubleValue = dictionaryValue as? Double {
model.setValue(NSNumber(double: doubleValue),forKey: propertyName)
}
case _ as String:
if let stringValue = dictionaryValue as? String {
model.setValue(stringValue,forKey: propertyName)
}
default:
print("\(propertyName) is an unkown optional value")
break
}
}
} else {
print("\(propertyName) is an unkown value")
if let objValue = dictionaryValue as? AnyObject {
model.setValue(objValue,forKey: propertyName)
}
}
default:
break
}
} else {
switch properValue {
case _ as Bool:
if let boolValue = dictionaryValue as? Bool {
model.setValue(NSNumber(bool:boolValue),forKey: propertyName)
}
case _ as Int:
if let intValue = dictionaryValue as? Int {
model.setValue(NSNumber(integer: intValue),forKey: propertyName)
}
case _ as Float :
if let floatValue = dictionaryValue as? Float {
model.setValue(NSNumber(float: floatValue),forKey: propertyName)
}
case _ as Double :
if let doubleValue = dictionaryValue as? Double {
model.setValue(NSNumber(double: doubleValue),forKey: propertyName)
}
case _ as String:
if let stringValue = dictionaryValue as? String {
model.setValue(stringValue,forKey: propertyName)
}
default:
break
}
}
}
}
}
}