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
- iOS开发之数据转模型(runtime)
- iOS runtime字典转模型
- iOS开发之关于runtime
- iOS开发之runtime详解
- iOS开发之runtime详解
- iOS开发之Runtime初探
- iOS开发之运行时编程(Runtime Programming)浅读
- iOS开发之旅--揭秘Runtime机制
- iOS开发RunTime之函数调用
- iOS开发教程之Objc Runtime笔记
- iOS开发之运行时Runtime
- iOS开发之Runtime常用示例总结
- iOS开发之Runtime常用示例总结
- iOS开发之Runtime常用示例总结
- iOS开发 -OC之 runtime机制
- iOS开发之Runtime机制深入解析
- iOS 开发RunTime之函数调用
- iOS开发之Runtime运行时机制
- C#中的分支结构:if..else if 、if... else 和 switch ...case语句的区别
- 获取前台进程包名(ForegroundProcess)
- Ubuntu16.04安装mysql
- 响应式开发(四)-----Bootstrap CSS----------Bootstrap CSS概览和相关注意事项
- 最小公倍数,最大公约数C语言(全)
- iOS开发之数据转模型(runtime)
- 10494Uva(二)高精度运算 易错点
- md5加密
- android 权限
- vue-cli
- TextView滚动显示内容
- BZOJ2527Meteors
- 2017.1.14【初中部 GDKOI】模拟赛B组 Mooo Moo 题解
- Go-作用域