iOS Runtime和KVC结合实现字典的数据模型转换

本文介绍了iOS开发中的Runtime和KVC。Runtime是一套底层API,是Objective-C动态语言的基础,主要涉及消息传递和转发。KVC则是通过Key名称动态访问和修改对象属性的技术,尤其在没有访问器方法的类中展现出优势。通过Runtime与KVC的结合,可以方便地实现字典到数据模型的转换。

1、runtime 是什么?

Runtime又叫运行时,是一套底层的C语言 API,其为iOS内部的核心之一,我们平时编写的OC代码,底层都是基于它来实现的。我们需要了解的是 Objective-C 是一门动态语言,它会将一些工作放在代码运行时才处理而并非编译时。也就是说,有很多类和成员变量在我们编译的时是不知道的,而在运行时,我们所编写的代码会转换成完整的确定的代码运行

2、runtime 可以干什么?

Runtime的特性主要是消息(方法)传递,如果消息(方法)在对象中找不到,就进行转发

3、KVC的定义

KVC(Key-value coding)键值编码,就是指在IOS的开发中,可以允许开发者通过Key名称直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存储方法。这样就可以在运行时动态地访问和修改对象的属性。

在实现了访问器方法的类中,使用点语法和KVC访问对象其实差别不大,二者可以任意混用。但是没有访问起方法的类中,点语法无法使用,这时KVC就有优势了。

3.1 KVC常用的方法

KVC的定义都是对NSObject的扩展来实现的,Objective-C中有个显式的NSKeyValueCoding类别名,所以对于所有继承了NSObject的类型,都能使用KVC(一些纯Swift类和结构体是不支持KVC的,因为没有继承NSObject),下面是KVC最为重要的四个方法:

- (nullable id)valueForKey:(NSString *)key;                          //直接通过Key来取值

- (void)setValue:(nullable id)value forKey:(NSString *)key;          //通过Key来设值

- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通过KeyPath来取值

- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通过KeyPath来设值

Runtime和KVC的结合可以实现字典的数据模型转换

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface TestModel : NSObject

@property (nonatomic, strong)NSString *name;
@property (nonatomic, strong)NSString *sex;
@property (nonatomic, strong)NSString *phone;
@property (nonatomic, strong)NSString *address;

- (instancetype)initWithDic:(NSDictionary *)dic;

@end

NS_ASSUME_NONNULL_END
#import "TestModel.h"
#import <objc/runtime.h>// 导入运行时文件

@implementation TestModel

- (instancetype)initWithDic:(NSDictionary *)dic
{
    if (self == [super init]) {
        NSArray *names = [self getProperties:[self class]];
        NSArray *types = [self getTypes:[self class]];
        
        for (NSInteger i = 0; i < names.count; i ++) {
            NSString *name = names[i];
            NSString *type = types[i];
            
            if ([type rangeOfString:@"NSArray"].location != NSNotFound) {
                [self setValue:@[] forKey:name];
            }else if ([type rangeOfString:@"NSDictionary"].location != NSNotFound) {
                [self setValue:@{} forKey:name];
            }else if ([type rangeOfString:@"NSString"].location != NSNotFound) {
                [self setValue:@"" forKey:name];
            }
        }
        
        // 字典可能是空
        if (![dic isKindOfClass:[NSDictionary class]]) {
            return self;
        }else {
            for (NSInteger i = 0; i < names.count; i ++) {
                NSString *name = names[i];
                
                // 如果字典中的值为空,赋值可能会出现问题
                if (dic[name] != nil && ![dic[name] isKindOfClass:[NSNull class]]) {
                    if ([dic[name] isKindOfClass:[NSNumber class]]) {
                        [self setValue:[NSString stringWithFormat:@"%@",dic[name]] forKey:name];
                    }else {
                        [self setValue:dic[name] forKey:name];
                    }
                }
                if ([name isEqualToString:@"ID"] && dic[@"id"] != nil && ![dic[@"id"] isKindOfClass:[NSNull class]]) {
                    [self setValue:dic[@"id"] forKey:name];
                }
                if ([name isEqualToString:@"descrip"] && dic[@"description"] != nil && ![dic[@"description"] isKindOfClass:[NSNull class]]) {
                    [self setValue:dic[@"description"] forKey:name];
                }
            }
        }
    }
    
    return self;
}

//返回当前类的所有属性
- (id)getProperties:(Class)cls{
    
    // 获取当前类的所有属性
    unsigned int count;// 记录属性个数
    objc_property_t *properties = class_copyPropertyList(cls, &count);
    // 遍历
    NSMutableArray *mArray = [NSMutableArray array];
    for (int i = 0; i < count; i++) {
        
        // An opaque type that represents an Objective-C declared property.
        // objc_property_t 属性类型
        objc_property_t property = properties[i];
        // 获取属性的名称 C语言字符串
        const char *cName = property_getName(property);
        // 转换为Objective C 字符串
        NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
        [mArray addObject:name];
    }
    
    return mArray.copy;
}
//返回当前类的所有属性类型
- (id)getTypes:(Class)cls{
    
    // 获取当前类的所有属性
    unsigned int count;// 记录属性个数
    objc_property_t *properties = class_copyPropertyList(cls, &count);
    // 遍历
    NSMutableArray *mArray = [NSMutableArray array];
    for (int i = 0; i < count; i++) {
        
        // An opaque type that represents an Objective-C declared property.
        // objc_property_t 属性类型
        objc_property_t property = properties[i];
        // 获取属性的名称 C语言字符串
        const char *cName = property_getAttributes(property);
        // 转换为Objective C 字符串
        NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
        [mArray addObject:name];
    }
    
    return mArray.copy;
}

在使用时只要调用这个类

NSDictionary *dic = @{@"name":@"小红",@"sex":@"女",@"phone":@"13478961265",@"address":@"长安街186号"};
    TestModel *model = [[TestModel alloc] initWithDic:dic];
    NSLog(@"输出:TestModel:%@",model);

打印结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值