使用Runtime的objc_msgSend实现模型和字典的互转
一、介绍
模型转字典,字典转模型,这是开发中最基本的功能。系统类中提供了一个setValuesForKeysWithDictionary方法来实现字典转模型,至于模型转字典,这个就需要使用runtime来实现了。其实字典和模型的互转可以完全使用运行时runtime来实现。典型的第三方有MJExtension和YYModel。现在我就来借助runtime的思想来进行大致的实现。
二、思路
- 字典转模型
- 遍历key
- objc_msgSend对key发送消息
- 模型转字典
- class_copyPropertyList获取属性列表properties
- 遍历properties,获取属性名name
- objc_msgSend对name发送消息,设置value
- 存入dic并返回
三、示例
person.h
// // Person.h // 运行时 // // Created by 夏远全 on 2019/10/11. // Copyright © 2019 北京华樾教育科技有限公司. All rights reserved. // #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface Person : NSObject @property (nonatomic, copy) NSString *age; @property (nonatomic, copy) NSString *name; /** 字典转模型 @param dic 字典 @return 模型 */ -(instancetype)initWithDictionary:(NSDictionary *)dic; /** 模型转字典 @return 字典 */ -(NSDictionary *)convertModelToDictionary; @end NS_ASSUME_NONNULL_END
person.m
// // Person.m // 运行时 // // Created by 夏远全 on 2019/10/11. // Copyright © 2019 北京华樾教育科技有限公司. All rights reserved. // #import "Person.h" #import <objc/message.h> @implementation Person -(instancetype)initWithDictionary:(NSDictionary *)dic{ self = [super init]; if (self) { for (NSString *key in dic.allKeys) { //创建一个set选择器 NSString *methodName = [NSString stringWithFormat:@"set%@:",key.capitalizedString]; SEL selector = NSSelectorFromString(methodName); //类型异常处理 id value = dic[key]; if([value isKindOfClass:[NSNull class]]) continue; //发送消息 if (!selector) continue; ((void (*)(id, SEL, id))objc_msgSend)(self, selector, value); } } return self; } -(NSDictionary *)convertModelToDictionary { NSMutableDictionary *dic = [NSMutableDictionary dictionary]; unsigned int count; objc_property_t *properties = class_copyPropertyList([self class], &count); for (int i=0; i<count; i++) { objc_property_t property = properties[i]; //属性名称 const char * name = property_getName(property); NSString *propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding]; //创建一个get选择器 SEL selector = NSSelectorFromString(propertyName); //发送消息 if (!selector) continue; id value = ((id (*)(id, SEL))objc_msgSend)(self, selector); //空与类型异常处理 if(!value || [value isKindOfClass:[NSNull class]]) continue; //值存储 [dic setValue:value forKey:propertyName]; } //copy创建的需要释放 free(properties); return dic; } @end
四、测试
1、dic -> model
-(void)test_objc_DicToModel { /// 这里主要提供一个思想,全部用字符串作为字段。其他类型:对象类型/NSNumber/int/float...,需要自己再去特殊处理 NSDictionary *dic = @{@"age":@"20",@"name":@"张三"}; Person *person = [[Person alloc] initWithDictionary:dic]; NSLog(@"age=%@,name=%@",person.age, person.name); }
2019-10-11 18:44:47.366662+0800 运行时[84069:2402809] age=20,name=张三
2、model -> dic
-(void)test_objc_ModelToDic { NSDictionary *dic = @{@"age":@"20",@"name":@"张三"}; Person *person = [[Person alloc] initWithDictionary:dic]; NSDictionary *dic2 = [person convertModelToDictionary]; NSLog(@"%@",dic2); }
2019-10-11 18:44:47.367024+0800 运行时[84069:2402809] { age = 20; name = "\U5f20\U4e09"; }
程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式!