根据前面的学习iOS开发之使用Runtime给Model类赋值和OC和Swift中的Runtime,总结一下将字典转换成模型(字典key不和属性一一对应,并且模型中有模型)
字典转模型
1、 创建分类
ls_assginToPropertyWithDictionary
在分类中实现,前面iOS开发之使用Runtime给Model类赋值功能,前面有详细的实现步骤,在这里不做重复,直接上代码
//
// NSObject+Model.m
// LSRuntimeOCDemo
//
// Created by a110 on 16/4/19.
// Copyright © 2016年 a110. All rights reserved.
//
#import "NSObject+Model.h"
@implementation NSObject (LSModel)
#pragma mark -- 通过字符串来创建该字符串的Setter方法,并返回
- (SEL) ls_creatSetterWithPropertyName: (NSString *) propertyName{
//1.首字母大写
propertyName = propertyName.capitalizedString;
//2.拼接上set关键字
propertyName = [NSString stringWithFormat:@"set%@:", propertyName];
//3.返回set方法
return NSSelectorFromString(propertyName);
}
/************************************************************************
*把字典赋值给当前实体类的属性
*参数:字典
*适用情况:当网络请求的数据的key与实体类的属性相同时可以通过此方法吧字典的Value
* 赋值给实体类的属性
************************************************************************/
-(void) ls_assginToPropertyWithDictionary: (NSDictionary *) data{
if (data == nil) {
return;
}
///1.获取字典的key
NSArray *dicKey = [data allKeys];
///2.循环遍历字典key, 并且动态生成实体类的setter方法,把字典的Value通过setter方法
///赋值给实体类的属性
for (int i = 0; i < dicKey.count; i ++) {
///2.1 通过getSetterSelWithAttibuteName 方法来获取实体类的set方法
SEL setSel = [self ls_creatSetterWithPropertyName:dicKey[i]];
if ([self respondsToSelector:setSel]) {
///2.2 获取字典中key对应的value
// NSString *value = [NSString stringWithFormat:@"%@", data[dicKey[i]]];
// 根据成员属性名去字典中查找对应的value
id value = data[dicKey[i]];
NSString *key=dicKey[i];
// 二级转换:如果字典中还有字典,也需要把对应的字典转换成模型
// 判断下value是否是字典
if ([value isKindOfClass:[NSDictionary class]]) {
// 根据字符串类名生成类对象
Class modelClass = NSClassFromString(key);
if (modelClass) { // 有对应的模型才需要转
// 把字典转模型
value = [modelClass ls_modelWithDictionary:value];
}
}
// 三级转换:NSArray中也是字典,把数组中的字典转换成模型.
// 判断值是否是数组
if ([value isKindOfClass:[NSArray class]]) {
// 判断对应类有没有实现字典数组转模型数组的协议
if ([self respondsToSelector:@selector(ls_arrayContainModelClass)]) {
// 转换成id类型,就能调用任何对象的方法
// id idSelf = self;
// 获取数组中字典对应的模型
NSString *type = [self ls_arrayContainModelClass][key];
// 生成模型
Class classModel = NSClassFromString(type);
NSMutableArray *arrM = [NSMutableArray array];
// 遍历字典数组,生成模型数组
for (NSDictionary *dict in value) {
// 字典转模型
id model = [classModel ls_modelWithDictionary:dict];
[arrM addObject:model];
}
// 把模型数组赋值给value
value = arrM;
}
}
if (value) { // 有值,才需要给模型的属性赋值
///2.3 把值通过setter方法赋值给实体类的属性
[self performSelectorOnMainThread:setSel
withObject:value
waitUntilDone:[NSThread isMainThread]];
}
}
}
}
///通过运行时获取当前对象的所有属性的名称,以数组的形式返回
const char *propertiesKey = "propertiesKey";
- (NSArray *) ls_allPropertyNames{
//参数一 关联到对象
//参数二 关联的属性key
//在oc 中 类的本质就是一个对象 将属性列表缓存
NSArray *plist = objc_getAssociatedObject(self, propertiesKey);
if(plist != nil)
{
return plist;
}
///存储所有的属性名称
NSMutableArray *allNames = [[NSMutableArray alloc] init];
///存储属性的个数
unsigned int propertyCount = 0;
///通过运行时获取当前类的属性
objc_property_t *propertys = class_copyPropertyList([self class], &propertyCount);
//把属性放到数组中
for (int i = 0; i < propertyCount; i ++) {
///取出第一个属性
objc_property_t property = propertys[i];
const char * propertyName = property_getName(property);
[allNames addObject:[NSString stringWithUTF8String:propertyName]];
}
///释放
free(propertys);
//5 设置关联对象
//参数1>关联的对象
//参数2>关联对象的key
//参数3>属性数值
//属性的持有方式 retain copy assign
objc_setAssociatedObject(self, propertiesKey, allNames, OBJC_ASSOCIATION_COPY_NONATOMIC);
return allNames;
}
#pragma mark -- 通过字符串来创建该字符串的Setter方法,并返回
- (SEL) ls_creatGetterWithPropertyName: (NSString *) propertyName{
//1.返回get方法: oc中的get方法就是属性的本身
return NSSelectorFromString(propertyName);
}
#pragma 返回属性和字典key的映射关系
-(NSDictionary *) ls_propertyMapDic{
return nil;
}
///返回数组转换模型的映射关系
-(NSDictionary *)ls_arrayContainModelClass{
return nil;
}
#pragma 根据映射关系来给Model的属性赋值
-(void) ls_assginToPropertyWithNoMapDictionary: (NSDictionary *) data{
///获取字典和Model属性的映射关系
NSDictionary *propertyMapDic = [self ls_propertyMapDic];
///转化成key和property一样的字典,然后调用assginToPropertyWithDictionary方法
NSArray *dicKey = [data allKeys];
NSMutableDictionary *tempDic = [[NSMutableDictionary alloc] initWithCapacity:dicKey.count];
for (int i = 0; i < dicKey.count; i ++) {
NSString *key = dicKey[i];
[tempDic setObject:data[key] forKey:propertyMapDic[key]];
}
[self ls_assginToPropertyWithDictionary:tempDic];
}
+ (instancetype)initWithDictionary: (NSDictionary *) data{
{
id objc = [[self alloc] init];
if (self) {
if ([objc ls_propertyMapDic] == nil) {
[objc ls_assginToPropertyWithDictionary:data];
} else {
[objc ls_assginToPropertyWithNoMapDictionary:data];
}
}
return objc;
}
}
+ (instancetype)ls_modelWithDictionary: (NSDictionary *) data{
return [self initWithDictionary:data];
}
#pragma mark -- 获取所有属性的值
- (void) ls_displayCurrentModleProperty{
//获取实体类的属性名
NSArray *array = [self ls_allPropertyNames];
//拼接参数
NSMutableString *resultString = [[NSMutableString alloc] init];
for (int i = 0; i < array.count; i ++) {
//获取get方法
SEL getSel = [self ls_creatGetterWithPropertyName:array[i]];
if ([self respondsToSelector:getSel]) {
//获得类和方法的签名
NSMethodSignature *signature = [self methodSignatureForSelector:getSel];
//从签名获得调用对象
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
//设置target
[invocation setTarget:self];
//设置selector
[invocation setSelector:getSel];
//接收返回的值
NSObject *__unsafe_unretained returnValue = nil;
//调用
[invocation invoke];
//接收返回值
[invocation getReturnValue:&returnValue];
[resultString appendFormat:@"%@\n", returnValue];
}
}
NSLog(@"%@", resultString);
}
@end
值得注意的是:
#pragma 返回属性和字典key的映射关系
-(NSDictionary *) ls_propertyMapDic{
return nil;
}
///返回数组转换模型的映射关系
-(NSDictionary *)ls_arrayContainModelClass{
return nil;
}
这两个方法,是必要时需要在自定义类中实现
上面的ls_assginToPropertyWithDictionary方法中,将之前的两篇文章结合。
2、创建自己的model
//
// Status.h
// LSRuntimeOCDemo
//
// Created by a110 on 16/4/19.
// Copyright © 2016年 a110. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NSObject+Model.h"
#import "sta.h"
@interface Status : NSObject
@property(nonatomic,copy)NSArray *aa;
@property(nonatomic,copy)sta *bb;
@property(nonatomic,copy)NSString *cc;
@end
//
// Status.h
// LSRuntimeOCDemo
//
// Created by a110 on 16/4/19.
// Copyright © 2016年 a110. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NSObject+Model.h"
#import "sta.h"
@interface Status : NSObject
@property(nonatomic,copy)NSArray *aa;
@property(nonatomic,copy)sta *bb;
@property(nonatomic,copy)NSString *cc;
@end
可以看出,在Status中既有数组又有sta模型。
需要在Status中实现
-(NSDictionary *)ls_arrayContainModelClass{
NSDictionary *dic=@{@"aa":@"sta"};
return dic;
}
-(NSDictionary *)ls_propertyMapDic{
return @{@"cc1":@"cc",@"aa":@"aa",@"bb":@"bb"};
}
//这里因为在ls_assginToPropertyWithDictionary方法中直接将value进行setter给key会报错,所以重写了setter方法,总觉得哪里不对,还需要学习
-(void)setBb:(sta *)bb{
_bb=bb;
}
测试一下:
NSDictionary* dic=@{@"cc1":@"ccccccccc",@"bb":@{@"status":@"status=1",@"name":@"name=2"},@"aa":@[@{@"status":@"status=1",@"name":@"name=2"}]};
Status *status=[Status ls_modelWithDictionary:dic];
NSLog(@"%@",status.aa);
打印结果
2016-04-22 17:29:17.371 LSRuntimeOCDemo[2801:280574] (
"<sta: 0x7f8791408150>"
)
模型转字典�
#pragma mark -- model转为字典
- (NSDictionary*) ls_dictionaryWithModel
测试
NSDictionary* dic=@{@"cc1":@"ccccccccc",@"bb":@{@"status":@"status=1",@"name":@"name=2"},@"aa":@[@{@"status":@"status=1",@"name":@"name=2"}]};
Status *status=[Status ls_modelWithDictionary:dic];
NSLog(@"%@",status.aa);
NSDictionary *dictionary=[status ls_dictionaryWithModel];
NSLog(@"%@",dictionary);
打印结果
2016-04-22 17:50:14.186 LSRuntimeOCDemo[2850:292737] {
aa = (
"<sta: 0x7f8a70d08070>"
);
bb = {
name = "name=2";
status = "status=1";
};
cc = ccccccccc;
}