美文网首页iOS 学习笔记Runtime
Runtime的用法(下):给Model赋值

Runtime的用法(下):给Model赋值

作者: Misaki_yuyi | 来源:发表于2018-01-17 23:09 被阅读113次

    最近心里太浮躁了,装了太多东西,还没有来的及写年终总结,先把上次遗漏的东西补上吧。上篇的传送门Runtime的用法(上)
    这次总结一下用runtime来给model赋值的问题。

    • 第一步 新建一个基类BaseModel,继承NSObject,里面写一些公共方法。
    #import "BaseModel.h"
    
    @implementation BaseModel
    
    #pragma mark - 通过字符串创建字符串的setter方法,并返回
    - (SEL)creatSetterWithPropertyName:(NSString *)properyName
    {
        //首字母大写
        properyName = properyName.capitalizedString;
        
        properyName = [NSString stringWithFormat:@"set%@:",properyName];
        
        return NSSelectorFromString(properyName);
    }
    
    #pragma mark - 把字典中的value赋值给实体类的属性
    - (void)assignToPropertyWithDictionary:(NSDictionary *)dictionary
    {
        if (dictionary == nil)
        {
            return ;
        }
        
        NSArray * keyArray = [dictionary allKeys];
        
        //循环遍历字典的key,动态生成实体类的setter方法,然后把字典的value通过setter方法赋值给实体类的属性
        for (int i = 0 ; i < keyArray.count ; i ++ )
        {
            //获取实体类的setter方法
            SEL setSel = [self creatSetterWithPropertyName:keyArray[i]];
            
            if ([self respondsToSelector:setSel])
            {
                //获取字典中key对应的value
                NSString * value = [NSString stringWithFormat:@"%@",dictionary[keyArray[i]]];
                
                //把value通过setter方法赋值给实体类的属性
                [self performSelectorOnMainThread:setSel withObject:value waitUntilDone:[NSThread isMainThread]];
            }
        }
    }
    

    然后给实体类提供初始化方法和构造器

    #pragma mark - 实例方法
    - (instancetype)initWithDictionary:(NSDictionary *)dictionary
    {
        self = [super init];
        if (self)
        {
            [self assignToPropertyWithDictionary:dictionary];
        }
        return self;
    }
    
    #pragma mark - 类方法返回一个实例
    + (instancetype)initWithDictionary:(NSDictionary *)dictionary
    {
        return [[self alloc] initWithDictionary:dictionary];
    }
    
    • 第二步 实例一个girlModel,继承自baseModel
    #import "BaseModel.h"
    
    @interface girlModel : BaseModel
    
    @property (nonatomic,copy) NSString * girl0;
    @property (nonatomic,copy) NSString * girl1;
    @property (nonatomic,copy) NSString * girl2;
    @property (nonatomic,copy) NSString * girl3;
    @property (nonatomic,copy) NSString * girl4;
    
    @end
    
    • 第三步 创建字典给model赋值
        NSMutableDictionary * dic = [[NSMutableDictionary alloc]initWithCapacity:11];
        for (int i = 0 ; i < 5 ; i ++ )
        {
            NSString * key = [NSString stringWithFormat:@"girl%d",i];
            NSString * value = [NSString stringWithFormat:@"我是第%d个女孩",i];
            [dic setObject:value forKey:key];
        }
    
        girlModel * model1 = [girlModel initWithDictionary:dic];
        NSLog(@"%@",model1.girl0);
        NSLog(@"%@",model1.girl1);
        NSLog(@"%@",model1.girl2);
        NSLog(@"%@",model1.girl3);
        NSLog(@"%@",model1.girl4);
    

    或者用setValuesForKeysWithDictionary来验证

        girlModel * model2 = [girlModel new];
        [model2 setValuesForKeysWithDictionary:dic];
        NSLog(@"%@",model2.girl0);
        NSLog(@"%@",model2.girl1);
        NSLog(@"%@",model2.girl2);
        NSLog(@"%@",model2.girl3);
        NSLog(@"%@",model2.girl4);
    

    上面的方法适用与字典中的key 和model属性名一一对应的情况。下面会给出如何去遍历Model中属性的值,并且给出字典的Key和Model的属性名不一样的情况我们该如何赋值。

    • 首先获取Model的实体属性
    #pragma mark - 通过runtime来获取model中所有属性的名称
    - (NSArray *)allPropertNames
    {
        //存储所有的属性名称
        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 * propertName = property_getName(property);
            [allNames addObject:[NSString stringWithUTF8String:propertName]];
        }
        //释放
        free(propertys);
        
        return allNames;
    }
    
    #pragma mark - 通过字符串来创建改字符串的setter方法,并返回
    - (SEL)creatGetterWithPropertyName:(NSString *)propertyName
    {
        //OC中getter方法就是属性本身
        return NSSelectorFromString(propertyName);
    }
    
    • 然后将model的属性动态的执行getter方法
    - (void)displayCurrentModelPropert
    {
        //获取实体类的属性名
        NSArray * array = [self allPropertNames];
        
        NSMutableString * resultString = [[NSMutableString alloc] init];
        
        for (int i = 0 ; i < array.count ; i ++ )
        {
            SEL getSel = [self creatGetterWithPropertyName:array[i]];
            
            if ([self respondsToSelector:getSel])
            {
                //获得类和方法的签名
                NSMethodSignature * signature = [self methodSignatureForSelector:getSel];
                
                //从签名获取调用对象
                NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:signature];
                
                //设置target 和 selector
                [invocation setTarget:self];
                
                [invocation setSelector:getSel];
                
                NSObject * __unsafe_unretained returnValue = nil;
                
                [invocation invoke];
                
                [invocation getReturnValue:&returnValue];
                
                [resultString appendFormat:@"%@\n",returnValue];
            }
        }
        NSLog(@"%@",resultString);
    }
    

    下面来验证一下

    [model1 displayCurrentModelPropert];
    

    输出结果


    屏幕快照 2018-01-17 下午8.56.45.png

    当字典的key和model中属性不一样,我们就需要用一个映射来维护。基类中添加一个返回映射字典的一个方法,然后在子类中进行重写,这个映射方法在基类中返回nil, 如果子类需要重写的话就对这个方法进行重写并返回映射字典

    #pragma mark - 构建隐射关系
    - (NSDictionary *)propertMap
    {
        return nil;
    }
    

    修改一下初始化方法,在有映射字典的情况和没有映射字典的情况下调用的方法是不一样的。

    #pragma mark - 实例方法
    - (instancetype)initWithDictionary:(NSDictionary *)dictionary
    {
        self = [super init];
        if (self)
        {
            if ([self propertMap] == nil)
            {
                [self assignToPropertyWithDictionary:dictionary];
            }
            else
            {
                [self assignToPropertyWithNoMapDictionary:dictionary];
            }
        }
        return self;
    }
    
    - (void)assignToPropertyWithNoMapDictionary:(NSDictionary *)dictionary
    {
        //获取字典和model属性的映射关系
        NSDictionary * propertyMapDictionary = [self propertMap];
        
        NSArray * dicKeyArray = [dictionary allKeys];
        
        NSMutableDictionary * tempDic = [[NSMutableDictionary alloc]initWithCapacity:dicKeyArray.count];
        
        for (int i = 0 ; i < dicKeyArray.count ; i ++ )
        {
            NSString * key = dicKeyArray[i];
            [tempDic setObject:dictionary[key] forKey:propertyMapDictionary[key]];
        }
        [self assignToPropertyWithDictionary:tempDic];
    }
    

    下面新建一个boyMdoel继承baseModel,并且重写映射关系

    @property (nonatomic,copy) NSString * boy0;
    @property (nonatomic,copy) NSString * boy1;
    @property (nonatomic,copy) NSString * boy2;
    @property (nonatomic,copy) NSString * boy3;
    @property (nonatomic,copy) NSString * boy4;
    
    - (NSDictionary *)propertMap
    {
        return @{@"key0":@"boy0",
                 @"key1":@"boy1",
                 @"key2":@"boy2",
                 @"key3":@"boy3",
                 @"key4":@"boy4",};
    }
    

    然后生成数据验证一下

    NSMutableDictionary * dic1 = [[NSMutableDictionary alloc]initWithCapacity:5];
    for (int i = 0 ; i < 5 ; i ++ )
    {
        NSString * key = [NSString stringWithFormat:@"key%d",i];
        NSString * value = [NSString stringWithFormat:@"我是第%d个男孩",i];
        [dic1 setObject:value forKey:key];
    }
    boyModel * model3 = [boyModel initWithDictionary:dic1];
    [model3 displayCurrentModelPropert];
    
    屏幕快照 2018-01-17 下午9.21.47.png

    今天写的有点多了,愿丢掉杂念,守一方净土

    相关文章

      网友评论

        本文标题:Runtime的用法(下):给Model赋值

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