利用runtime实现更加灵活的KVC

   字典转模型在iOS开发中属于非常常见的操作.

   比较简单的方式是 KVC ,但是这样会有一个局限 ,就是 模型中的属性必须和字典中的完全匹配,否则,KVC将会报错.

   手动编写代码虽然比较灵活,但是如果字典中数据量庞大,也会是一个糟糕的体验.

   针对以上问题,利用runtime实现一个更加灵活的字典转模型.


  1.为 NSObject建立一个分类

    NSObject+GQRuntimeTool.h文件

#import <Foundation/Foundation.h>

@interface NSObject (GQRuntimeTool)

-(void)objectWithDictionary:(NSDictionary*)dict;

@end
  NSObject+GQRuntimeTool.m文件


#import "NSObject+GQRuntimeTool.h"
#import <objc/runtime.h>
#import <objc/message.h>

@implementation NSObject (GQRuntimeTool)


-(void)objectWithDictionary:(NSDictionary*)dict
{
    Class cls = [self class];
    unsigned int count;
    //Ivar instance variable 就是类的成员属性
    //通过运行时函数 获得 类内部的成员属性个数 放到 count 的地址
    //返回说是一个指针 ,所指向的内存空间为 首个 Ivar 的内存地址,这个相当于数组名
    Ivar *ivarArr = class_copyIvarList(cls, &count);
    //遍历数组( 数组名存放的是数组首地址,就是一个指针)
    for(int i =0;i<count;i++)
    {
        //把 Ivar 当成一个基本类型 比如 double 或者 int
        Ivar ivar = ivarArr[i];
        //获取属性名
        const char* name = ivar_getName(ivar);
        //获取属性类型
        NSString* typeName =[NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
      
        NSLog(@"%@",typeName); //发现 int 的类型是 i
        
        //1.将 c 字符串转换成 OC 的
        NSMutableString * ocVaribaleName = [NSMutableString stringWithUTF8String:name];
        //2.删除前面的  _
        [ocVaribaleName deleteCharactersInRange:NSMakeRange(0, 1)]; //    id_str
        //3.从字典取值的时候,使用的 key
        NSString *key = [ocVaribaleName copy];
        //4.取值
        id value = dict[key];
        //5.将 id_str 转换成 setId_Str:
        NSString * setter =  [self setterNameWithCString:name];
        //6.使用运行时填充数据
        if ([typeName isEqualToString:@"i"]) {
            int intValue =[value intValue];
            objc_msgSend(self, NSSelectorFromString(setter),intValue);
        }else if([typeName isEqualToString:@"@\"NSString\""])
        {
            NSString *str = [NSString stringWithFormat:@"%@",value];
            objc_msgSend(self, NSSelectorFromString(setter),str);
        }//如果有其他类型,需要自己适配一下...
        /**
         如果有其他类型,需要自己适配一下
         
         int       i
         double    d
         bool      B
         
         */
        
    
    }

}

/**
 *  利用c字符串获得 setter方法的名字
 *
 */
-(NSString*)setterNameWithCString:(const char*) cstr
{
    char * strPointer = NULL;
    
    //此处不用 oc自带capitalizedString 的原因是发现他会将 id_str 大写成 Id_Str 而不是 Id_str
    CapicializeString(cstr, &strPointer);
    
    NSString * setter = [NSString stringWithUTF8String:strPointer];
    
    free(strPointer);
    
    return setter;

}

void CapicializeString( const char * string,char ** s){
    
    (*s) =  malloc(strlen(string)+6);
    //拼出格式,并去掉 _  现在的格式是   setid_str
    sprintf((*s),"set%s:",(string+1));
    //大写 set后面的第一个字母
    if(('a'<=(*s)[3])&&((*s)[3]<='z')) (*s)[3]-=32;
    
}


@end

2. 模型对象

GQModel.h

#import <Foundation/Foundation.h>
#import "GQToy.h"

@interface GQModel : NSObject
/**
 *  唯一标识符
 */
@property (nonatomic,copy)NSString *id_str;
/**
 *  姓名
 */
@property (nonatomic,copy)NSString *name;
/**
 *  年龄
 */
@property (nonatomic,assign)int age;
/**
 *  性别
 */
@property (nonatomic,copy)NSString *sex_str;


@end

.m文件无内容


3.在controller中

- (void)viewDidLoad
{
    [super viewDidLoad];
   
  //这个字典在实际工作中,都是来自 JSon对象的,此处仅仅供测试
   NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                         @"22",@"id_str",
                         @33,@"age",
                         @"Grace",@"name",
                         @"male",@"sex_str",
                         nil];
    
    GQModel * mo = [[GQModel alloc]init];
    [mo objectWithDictionary:dict];
   
    if (mo) {
        NSLog(@"mo的id是:%@\n",mo.id_str);
        NSLog(@"mo的名字是:%@\n",mo.name);
        NSLog(@"mo的年龄是:%d\n",mo.age);
        NSLog(@"mo的性别是:%@\n",mo.sex_str);
        
    }
    
}

运行打印结果如下:


如果以后字典数据发生变化,只需要修改模型中的属性名即可.

是不是非常简单呢?





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值