美文网首页
Objective-C-通过Runtime进行模型转换

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

作者: SuperDawn_0828 | 来源:发表于2017-09-24 16:45 被阅读11次

    一、写在前面

    接上篇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

    相关文章

      网友评论

          本文标题:Objective-C-通过Runtime进行模型转换

          本文链接:https://www.haomeiwen.com/subject/keftextx.html