Swift中的反射

关键词

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
                        }
                    }
                }

            }
        }
    }

源码地址:https://github.com/VictorWuSH/SwiftExercise

相关文章

软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘...
现实生活中,我们听到的声音都是时间连续的,我们称为这种信...
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿...
【Android App】实战项目之仿抖音的短视频分享App(附源码和...
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至...
因为我既对接过session、cookie,也对接过JWT,今年因为工作...