iOS中字典转模型的方法及底层原理

来源:互联网 发布:linux more 到最后 编辑:程序博客网 时间:2024/05/21 19:22

1 自动打印属性字符串分类

  • 提供一个分类,专门根据字典生成对应的属性字符串。
@implementation NSObject (Property)+ (void)PH_createPropertyCodeWithDict:(NSDictionary *)dict{    NSMutableString *strM = [NSMutableString string];    // 遍历字典    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull propertyName, id  _Nonnull value, BOOL * _Nonnull stop) {        NSLog(@"%@,%@",propertyName,[value class]);        NSString *code;        if ([value isKindOfClass:NSClassFromString(@"__NSCFString")]) {            code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSString *%@;",propertyName]            ;        } else if ([value isKindOfClass:NSClassFromString(@"__NSCFNumber")]){            code = [NSString stringWithFormat:@"@property (nonatomic, assign) int %@;",propertyName]            ;        }else if ([value isKindOfClass:NSClassFromString(@"__NSCFArray")]){            code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSArray *%@;",propertyName]            ;        }else if ([value isKindOfClass:NSClassFromString(@"__NSCFDictionary")]){            code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSDictionary *%@;",propertyName]            ;        } else if ([value isKindOfClass:NSClassFromString(@"__NSCFBoolean")]){            code = [NSString stringWithFormat:@"@property (nonatomic, assign) BOOL %@;",propertyName]            ;        }        [strM appendFormat:@"\n%@\n",code];    }];    NSLog(@"%@",strM);}@end

2 字典转模型的方式一:KVC

@implementation Status+ (instancetype)statusWithDict:(NSDictionary *)dict{    Status *status = [[self alloc] init];    [status setValuesForKeysWithDictionary:dict];    return status;}@end
  • KVC字典转模型弊端:必须保证,模型中的属性和字典中的key一一对应。
    • 如果不一致,就会调用[<Status 0x7fa74b545d60> setValue:forUndefinedKey:]
      key找不到的错。
    • 分析:模型中的属性和字典的key不一一对应,系统就会调用setValue:forUndefinedKey:报错。
    • 解决:重写对象的setValue:forUndefinedKey:,把系统的方法覆盖,
      就能继续使用KVC,字典转模型了。
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{}

3 字典转模型的方式二:Runtime

  • KVC:遍历字典中所有key,去模型中查找有没有对应的属性名
  • runtime:遍历模型中所有属性名,去字典中查找

1.思路与步骤

<!--* 思路:利用运行时,遍历模型中所有属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值。--><!--* 步骤:提供一个NSObject分类,专门字典转模型,以后所有模型都可以通过这个分类转。-->

2.分类

+ (instancetype)PH_modelWithDict:(NSDictionary *)dict{    // 创建对应类的对象    id objc = [[self alloc] init];    // runtime:遍历模型中所有成员属性,去字典中查找    // 属性定义在哪,定义在类,类里面有个属性列表(数组)    // 遍历模型所有成员属性    // ivar:成员属性    // class_copyIvarList:把成员属性列表复制一份给你    // Ivar *:指向Ivar指针    // Ivar *:指向一个成员变量数组    // class:获取哪个类的成员属性列表    // count:成员属性总数    unsigned int count = 0;    Ivar *ivarList = class_copyIvarList(self, &count);    for (int i = 0; i < count; i++) {        // 获取成员属性        Ivar ivar = ivarList[i];        // 获取成员名        NSString *propertyName = [NSString stringWithUTF8String:ivar_getName(ivar)];        //成员属性类型        NSString *propertyType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];        //获取key        NSString *key = [propertyName substringFromIndex:1];        //获取字典的value        id value = dict[key];        // 给模型的属性赋值        // value:字典的值        // key:属性名// user:NSDictionary        //** '二级转换'**          // 值是字典,成员属性的类型不是字典,才需要转换成模型        if ([value isKindOfClass:[NSDictionary class]] && ![propertyType containsString:@"NS"]) {            // 需要字典转换成模型            // 转换成哪个类型            // @"@\"User\"" User            NSRange range = [propertyType rangeOfString:@"\""];            propertyType = [propertyType substringFromIndex:range.location + range.length];            // User\"";            range = [propertyType rangeOfString:@"\""];            propertyType = [propertyType substringFromIndex:range.location];            // 字符串截取            // 获取需要转换类的类对象            Class modelClass = NSClassFromString(propertyType);            if (modelClass) {                value = [modelClass PH_modelWithDict:value];            }        }<!-- 三级转换:NSArray中也是字典,把数组中的字典转换成模型.-->        // 判断值是否是数组        if ([value isKindOfClass:[NSArray class]]) {            //判断对应类有没有实现字典数组转模型数组的协议            if ([self respondsToSelector:@selector(PH_arrayContainModelClass)]) {                // 转换成id类型,就能调用任何对象的方法                id idSelf = self;                // 获取数组中字典对应的模型                NSString *type =  [idSelf PH_arrayContainModelClass][key];                // 生成模型                Class classModel = NSClassFromString(type);                NSMutableArray *arrM = [NSMutableArray array];                // 遍历字典数组,生成模型数组                for (NSDictionary *dict in value) {                    // 字典转模型                    id model = [classModel PH_modelWithDict:dict];                    [arrM addObject:model];                }                // 把模型数组赋值给value                value = arrM;            }        }        if (value) {            // kvc赋值:不能传空            [objc setValue:value forKey:key];        }        NSLog(@"%@",key);        NSLog(@"%@,%@",propertyName,propertyType);    }    return objc;}

3.转换

- (void)viewDidLoad {    [super viewDidLoad];    // 解析    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil];    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];    NSArray *dictArr = dict[@"statuses"];    // 设计模型属性代码    NSMutableArray *statuses = [NSMutableArray array];    for (NSDictionary *dict in dictArr) {        // 字典转模型        Status *status = [Status PH_modelWithDict:dict];        [statuses addObject:status];    }    NSLog(@"%@",statuses);

}

KVC: Key Value Coding (键值编码)

在iOS开发中,KVC是我们经常要使用的技术.那么KVC有什么作用呢?简单列举一下下面几种:

  • 取值和赋值(开发中基本不用)
  • 获取对象私有变量的值.(经常使用,例如UIPageContorl分页, 设置圆点为图片)
  • 改变对象私有变量的值(经常使用)
  • 简单的字典转模型(偶尔使用)
  • 模型转字典
  • 批量取值

KVC字典转模型的底层实现

  • 通常我们手动将字典转模型的话,会在模型中提供一个类方法接收一个字典,在这个方法中将字典转换成模型,再将转换好的模型返回.

    + (instancetype)statusWithDict:(NSDictionary *)dict{  Status *status = [[self alloc] init];  //利用KVC字典转模型  [status setValuesForKeysWithDictionary:dict];  return status;}
  • 分析一下[status setValuesForKeysWithDictionary:dict]的底层实现原理

    + (instancetype)statusWithDict:(NSDictionary *)dict{  Status *status = [[self alloc] init];  //利用KVC字典转模型  //[status setValuesForKeysWithDictionary:dict];  //setValuesForKeysWithDictionary:原理--遍历字典中所有的key,去模型中查找对应的属性,把值给模型属性赋值  [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {      // 这行代码才是真正给模型的属性赋值      [status setValue:obj forKey:key];  }];  return status;}
  • KVC字典转模型弊端:必须保证,模型中的属性和字典中的key一一对应。如果不是一一对应的话,就会报错,仔细看一下错误信息,[<Status 0x7fd439d20a60> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key source.是系统调用了setValue:forUndefinedKey:报错.如果想解决这个问题,只需要在模型中重写对象的setValue:forUndefinedKey:,把系统的方法覆盖, 就能继续使用KVC,字典转模型了。
    - (void)setValue:(id)value forUndefinedKey:(NSString *)key{}

啰嗦一点KVC的setValue:forKey:方法赋值的原理

  • 首先会去模型中查找有没有对应key的setter方法,有就直接调用set方法方法赋值.
  • 上一步没有的话,去模型中查找有没有和key同名的属性,有的话赋值给与key同名的属性.
  • 上一步还没有的话,去属性中查找有没有和key同名的带下划线的属性,有的话直接赋值.
  • 如果再没有,那就直接调用对象的 setValue:forUndefinedKey:直接报错

原创粉丝点击