iOS开发之数据转模型(runtime)

来源:互联网 发布:淘宝预热 编辑:程序博客网 时间:2024/06/16 16:26

一般的json数据转模型都是用的KVC,用数据中的值作为key去匹配模型,通过

setValue(value:, forKey: )

方法来生成模型数据,但是这样的方法有一个很大的弊端就是如果数据中的key在模型中不存在那么就会报错,所以要解决这个问题就应该反过来,通过模型中的属性去数据中查找,这里就需要用到runtime,
首先定义一些关系复杂model类

class Person: NSObject {    var name:String?    var age:NSNumber?    var idCard:Card?    var persons:[Person]?}class Student: Person {    var Class:Int?}class Card: NSObject {    var id:String?}

以上模型类需要解决的问题是,Student继承自Person,persons是个数组,类型是Person,idCard是另一个类,基本上平时会碰到的数据结构大概就是这样,层次可能更多,但是类型基本不会出这几种。
要实现以上model的数据转换,可以为NSObject创建一个extension,在其中实现字典转模型
思路是这样的:
1.用runtime获取模型的所有属性名称,存放在一个数组中
2.获取到的属性可能除了一些基本属性以外还会有特殊的,例如数组,自定义类,自定义类的子类等,自定义类型的数组等等等等,所以存放属性名称的数组最好是自定义的一个类型的属性,
首先,自定义一个类来存储获取到的属性名称,以及一些关于属性名称的判断,

/// 自定义的获取到的属性类class CHProperty{    //属性名字    var propertyNmae:NSString!    //属性名字对应的key    var key:String!    //属性    var property:objc_property_t    //属性类型    var propertyType:CHType!    init(property:objc_property_t){        self.property = property        self.propertyNmae = NSString(cString: property_getName(property), encoding: String.Encoding.utf8.rawValue)        key = self.propertyNmae as String        //自定义的类的Types格式为T@"_TtC15字典转模型4Card",N,&,Vcard        //T+@+"+..+工程的名字+数字+类名+"+,+其他,而我们想要的只是类名,所以要修改这个字符串        var code: NSString = NSString(cString: property_getAttributes(property), encoding: String.Encoding.utf8.rawValue)!        //直接取出""中间的内容        code = code.components(separatedBy: "\"")[1] as NSString        let bundlePath = getBundleName()        let range = code.range(of: bundlePath)        if range.length > 0{            //去掉工程名字之前的内容            code = code.substring(from: range.length + range.location) as NSString        }        //在去掉剩下的数字        var number:String = ""        for char in (code as String).characters{            if char <= "9" && char >= "0"{                number += String(char)            }else{                break            }        }        let numberRange = code.range(of: number)        if numberRange.length > 0{            //得到类名            code = code.substring(from: numberRange.length + numberRange.location) as NSString        }        self.propertyType = CHType(code: code)    }}class CHType {    //类名字    var code:NSString    //类的类型    var typeClass:AnyClass?    //是否属于Foundtation框架    var isFromFoundtion:Bool = true    //是否是数组    var isArray:Bool = false    //数组里面存放的类型    var arrayClass:AnyClass?    init(code:NSString){        self.code = code        //判断是否属于Foundtation框架        if self.code.hasPrefix("NS"){            self.typeClass = NSClassFromString(self.code as String)            self.isFromFoundtion = true            if self.code.hasPrefix("NSArray"){                self.isArray = true            }        }else{            //如果是自定义的类NSClassFromString这个方法传得字符串是工程的名字+类名            self.typeClass = getClassWith(self.code as String)            self.isFromFoundtion = false        }    }}

通过CHType类可以通过类型名来获取类是否是Foundtation类,是否是数组,如果是数组,可以知道数组中的是什么类型,
CHProperty类可以存储属性名字,通过获取底层字符串可以获取到包含类名的一串字符串,通过字符串的处理可以得到类名

//获取工程的名字func getBundleName() -> String{    var bundlePath = Bundle.main.bundlePath    bundlePath = bundlePath.components(separatedBy: "/").last!    bundlePath = bundlePath.components(separatedBy: ".").first!    return bundlePath}//通过类名返回一个AnyClassfunc getClassWith(_ className:String) ->AnyClass?{    let type = getBundleName() + "." + className    return NSClassFromString(type)}

开始将一个数据转换成模型

//获取模型 ,数据转模型方法的入口    class func createModelWith(_ dictionary:NSDictionary) -> AnyObject{        let model = self.init()        //获取所有的属性        let properties = self.getProperties()        model.setValue(dictionary, forKeys: properties)        return model    }

主要是封装的

setValue(_ values:, forKeys properties:)

方法

//把一个字典里的值赋给一个对象的值    func setValue(_ values:NSDictionary, forKeys properties:[CHProperty]?){        //判断属性数组是否存在        if let _ = properties{            for property in properties!{                //判断该属性是否属于Foundtation框架                if property.propertyType.isFromFoundtion {                    if let value = values[property.key]{                        //判断是否是数组,若是数组,判断数组里装的类是否是自定义类                        if property.propertyType.isArray && property.propertyType.arrayClass != nil && value is NSArray{                            //把字典数组转换成模型数组                            let temp = property.propertyType.arrayClass!.convert(value as! NSArray)                            //为model类赋值                            print("\(property.propertyNmae!):\(temp)")                            self.setValue(temp, forKey: property.propertyNmae as String)                        }else{                            //为model类赋值                            print("\(property.propertyNmae!):\(value)")                            self.setValue(value, forKey: property.propertyNmae as String)                        }                    }                }else{                    if let value = values[property.key]{                        if value is NSDictionary{                            let subClass = property.propertyType.typeClass?.createModelWith(value as! NSDictionary)                            //为model类赋值                            self.setValue(subClass, forKey: property.propertyNmae as String)                        }                    }                }            }        }    }

中间碰到是数组的数据类型使用

//把一个字典数组转成一个模型数组    class func convert(_ array:NSArray) -> [AnyObject]{        var temp = Array<AnyObject>()        let properties = self.getProperties()        for i in 0 ..< array.count {            let keyValues = array[i] as? NSDictionary            if (keyValues != nil){                let model = self.init()                //为每个model赋值                model.setValue(keyValues!, forKeys: properties)                temp.append(model)            }        }        return temp    }

解决数组的问题
特别注意的是如果有数组,或者model的属性跟关键字冲突的时候可以重写

 //子类重写这个方法,对字典里的key和类的属性进行映射    func replacedKeyFromPropertyName() ->[String:String]{        return ["":""]    }    //子类重写这个方法,说明数组里存放的数据类型    func objectClassInArray() -> [String:String]{        return ["":""]    }

这两个方法来解决

1 0
原创粉丝点击