Objective-C-通过Runtime进行模型转换

一、写在前面

接上篇Objective-C之runtime学习笔记简单的介绍了Runtime使用,这里着重介绍下如如何使用Runtime进行字典到模型的转换。

二、通过分类获取类的属性及属性类型

上篇中举例了MusicAlbum类,通过Runtime获取类的属性和属性的类型,但是这种方法比较繁琐,换一个类就要重新写重复的代码。因此我们可以通过对NSObject 创建一个分类,获取类的属性及属性类型。

@interface NSObject (Property)
+ (NSArray *)objectPropertyAndType;
@end

@implementation NSObject (Property)
+ (NSArray *)objectPropertyAndType
{
    unsigned int count = 0;
    objc_property_t *propertys = class_copyPropertyList([self class], &count);
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:0];
    for (int i = 0; i < count; i++) {
        objc_property_t property = propertys[i];
        NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
        NSString *propertyAttri = [NSString stringWithUTF8String:property_getAttributes(property)];
        [array addObject:@{@"propertyName": propertyName, @"propertyAttri": propertyAttri}];
    }
    return [array copy];
}
@end

通过MusicAlbum类调用这个分类方法:

NSArray *array = [MusicAlbum objectPropertyAndType];
NSLog(@"array %@", array);

三、对类的属性及类型进一步封装

上述分类方法是将类的属性及类型放到了字典中。属性的基本信息包括属性名及属性类型,可以创建一个类,用来承载类的属性信息。

@interface LMProperty : NSObject
@property (nonatomic, assign, readonly) objc_property_t property;
@property (nonatomic, strong, readonly) NSString *name;
@property (nonatomic, strong, readonly) LMPropertyType *type;

+ (instancetype)propertyKeyWithProperty:(objc_property_t)property;

@end

通过对property的处理得到属性名及属性类型编码。

- (void)variableNameAndType:(objc_property_t)property
{
    NSString *name = [NSString stringWithUTF8String:property_getName(property)];
    NSString *arrributes = [NSString stringWithUTF8String:property_getAttributes(property)];

    self.name = name;
    self.type = [LMPropertyType propertyTypeWithCode:arrributes];
}

LMPropertyType类是用来承载属性的类型信息。通过Runtime只能得到属性类型的编码,因此我们要对属性编码进一步处理。

@interface LMPropertyType : NSObject
//属性类型
@property (nonatomic, strong) NSString *type;
//属性类型编码
@property (nonatomic, strong) NSString *code;
//是否是基本数据类型
@property (nonatomic, readonly, getter=isNumberType) BOOL numberType;
//是否是BOOL类型
@property (nonatomic, readonly, getter=isBoolType) BOOL boolType;
//是否是id类型
@property (nonatomic, readonly, getter=isIdType) BOOL idType;
//是否来自Foundation框架
@property (nonatomic, readonly, getter=isFromFoundation) BOOL fromFoundation;
//属性类型的类,如果是基本数据类型,则为nil
@property (nonatomic, readonly) Class typeClass;

+ (instancetype)propertyTypeWithCode:(NSString *)code;

@end

通过LMPropertyType处理属性类型编码,获取属性类型是基本数据类型还是引用数据类型及具体的类型信息。

四、实现字典到模型的转换

NSObject+Property进行处理,添加两个类方法,实现字典到模型的转化及数组到模型数组的转换。

+ (instancetype)valueFromKeyValue:(id)keyValue;
+ (NSMutableArray *)valueArrayFromArray:(NSArray *)array;

先获取类的属性信息,再属性信息及字典中对应的值进行处理:

//获取类的属性信息
- (NSArray *)getObjectPropertyAndType
{
    unsigned int count = 0;
    objc_property_t *propertys = class_copyPropertyList([self class], &count);
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count; i++) {
        objc_property_t property = propertys[i];
        LMProperty *propertyKey = [LMProperty propertyKeyWithProperty:property];
        [array addObject:propertyKey];
    }
    return array;
}

//对属性和字典的值作对比,通过KVC对类的属性赋值
- (void)setObjectPropertyList:(NSArray *)array keyValue:(id)keyValue
{
    [array enumerateObjectsUsingBlock:^(LMProperty *property, NSUInteger idx, BOOL * _Nonnull stop) {
        NSString *name = property.name;
        LMPropertyType *type = property.type;
        id value = keyValue[name];
    
        if (!type.fromFoundation && type.typeClass) {
            if (value != nil) {
                value = [type.typeClass valueFromKeyValue:value];
            }
        } else if (type.typeClass && type.typeClass == [NSArray class]) {
            NSDictionary *arrayKeyValue = [self arrayValueForKeyValue];
            NSString *arrayElementClassString = arrayKeyValue[name];
            if (arrayElementClassString) {
                value = [NSClassFromString(arrayElementClassString) valueArrayFromArray:array];
            } else {
                value = value;
            }
        } else if (type.typeClass == [NSString class]) {
            if ([value isKindOfClass:[NSNumber class]]) {
                value = [value absoluteString];
            } else if ([value isKindOfClass:[NSURL class]]) {
                value = [value absoluteString];
            }
        } else if ([value isKindOfClass:[NSString class]]) {
            if (type.typeClass == [NSURL class]) {
                value = [NSURL URLWithString:value];
            } else if (type.isNumberType) {
                if (type.isBoolType) {
                    if ([value isEqualToString:@"yes"] || [value isEqualToString:@"true"]) {
                        value = @YES;
                    } else {
                        value = @NO;
                    }
                } else {
                    value = [[[NSNumberFormatter alloc] init] numberFromString:value];
                }
            }
        } else if (type.isNumberType) {
            if (![value isKindOfClass:[NSNumber class]]) {
                value = @0;
            }
        }
    
        [self setValue:value forKey:name];
    }];
}

上述代码详细列举了字典中的值到对象属性之间的转换,主要参考了小码哥的MJExtension,如有谬误请指正。
Demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • //我所经历的大数据平台发展史(三):互联网时代 • 上篇http://www.infoq.com/cn/arti...
    葡萄喃喃呓语阅读 51,216评论 10 200
  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,721评论 7 64
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,554评论 33 466
  • 我是青年,我脱不了青年所共有的东西。父母爱我,我也爱父母;有人爱我,我也爱他们;我爱别人,别人不一定爱我。我不知...
    Bobercheng阅读 366评论 0 0