美文网首页闻道丶iOS(尝鲜版)iOS学习笔记iOS Developer
04 runtime用法之动态添加属性和字典转模型

04 runtime用法之动态添加属性和字典转模型

作者: 小码码 | 来源:发表于2017-03-27 18:58 被阅读63次

    1 动态添加属性

    若想给系统的类添加属性,可以采用Runtime的方法,比如:
    给系统的NSObject类添加一个name属性,过程如下:
    1) 创建一个NSObject的分类,NSObject+Property

    2)在NSObject+Property.h文件中声明属性
    @property NSString *name;

    备注: 在分类中是不能添加属性的,
          常用@property方法在分类中只会生成get,set方法声明,并不会生成get,set方法实现和下划线成员变量,所以此处简写成@property NSString *name;声明一下即可。
    

    3)在NSObject+Property.m文件中采用runtime方法实现

    - (void)setName:(NSString *)name
    {
        // 第一个参数:给哪个对象产生关联
        // 第二个参数:属性名
        // 第三个参数:属性值
        // 第四个参数:策略
        objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (NSString *)name
    {
        return objc_getAssociatedObject(self, "name");
    }
    

    2 自动添加属性的方法

    在将字典转成模型的过程中,如果字典中包含很多key,并且这些key都要转成相应的模型,此时手动在模型的.h文件中添加property属性,也是一项繁琐的工作,下面是如何快速生成属性列表的方法:
    1)新建一个NSDictionary的分类
    2)提供一个方法,在该方法中实现代码:

    - (void)PropertyCode
    {
        // 生成多少个属性代码 => 字典key
        // 创建可变字符串
        NSMutableString *codes = [NSMutableString string];
        // 私有API:苹果没有暴露出来的类
        // 遍历字典
        [self enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull value, BOOL * _Nonnull stop) {
            
            NSString *code ;
    
            if ([value isKindOfClass:[NSString class]]) {
                //  NSString
                code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSString *%@;",key];
            } else if ([value isKindOfClass:[NSArray class]]) {
                // NSArray
                code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSArray *%@;",key];
            } else if ([value isKindOfClass:[NSDictionary class]]) {
                // NSDictionary
                code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSDictionary *%@;",key];
            } else if ([value isKindOfClass:NSClassFromString(@"__NSCFBoolean")]) {         
                code = [NSString stringWithFormat:@"@property (nonatomic, assign) BOOL %@;",key];          
            } else if ([value isKindOfClass:[NSNumber class]]) {            
                code = [NSString stringWithFormat:@"@property (nonatomic, assign) NSInteger %@;",key];
            }        
            [codes appendFormat:@"\n%@\n",code];
            
        }];    
        NSLog(@"%@",codes);
    }
    
    1. 获取属性列表
      在获取字典的地方调用[dict PropertyCode];即可 NSLog(@"%@",codes);codes就是属性列表,直接拷贝到模型的.h文件中即可。

    3 字典转模型

    3.1 KVC的方法

    // 1.创建模型对象
    Status *status = [[self alloc] init];

    // 2.调用KVC方法:setValuesForKeysWithDictionary
    [status setValuesForKeysWithDictionary:dict];

    缺点:必须要保证模型的属性跟字典的key一一对应,否则会崩溃

    setValuesForKeysWithDictionary底层的实现原理:
    
    // 1.快速遍历字典
    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
          NSLog(@"%@ %@",key,obj);
          // 2.去模型中查找有没有key对应的set方法,如果有,直接调用方法赋值,
          // 3.没有set方法,就去模型中查找有没有key对应的成员变量,如果有,直接给key赋值
          // 4.没有key对应的成员变量,就去模型中查找有没有_key,有,直接给_key赋值
    
    // 5.     2,3,4    都没有时就调用- (void)setValue:(id)value forUndefinedKey:(NSString *)key方法,查不到的错误
    
    备注:如果不想系统报错,可以重写- (void)setValue:(id)value forUndefinedKey:(NSString *)key{} (不推荐)
    

    3.2 runtime的方法(不存在崩溃的问题)

    1) 自定义一个NSObject的分类
    2)提供一个方法

    // 字典转模型
    + (instancetype)modelWithDict:(NSDictionary *)dict
    {
        // 1.创建模型对象
        id objc = [[self alloc] init];    
        // 把字典中的value给模型中属性赋值
        // runtime:1.遍历模型中成员变量,2.去字典中查找对应的value3.给模型中属性赋值
        // ivar:成员变量
        // 第一个参数:获取哪个类的成员变量
        // 第二个参数:类中成员变量总数
        unsigned int count;
        // 获取成员变量数组:注意:只会获取当前类的属性,不会获取父类
        Ivar *ivarList = class_copyIvarList(self, &count);
          for (int i = 0; i < count; i++) {
            // 获取成员变量
            Ivar ivar = ivarList[i];
            // 获取成员变量名字
            NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
            // 获取成员变量类型
            NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
            // @\"User\"=> User
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
            // 获取key:user
            NSString *key = [ivarName substringFromIndex:1];
            // 获取字典的value:NSDictionary
            id value = dict[key];
              // 二级:如果是字典,转模型
            if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {
                 // 根据类名字符串转换成类对象
                Class modelClass = NSClassFromString(ivarType);
                // 字典转模型
               value = [modelClass modelWithDict:value];
          }
            // 给模型中属性赋值
            [objc setValue:value forKey:key];        
        }    
        return objc;
    }
    

    3)调用方法
    // 字典转模型
    Status *s = [Status modelWithDict:dict];

    相关文章

      网友评论

        本文标题:04 runtime用法之动态添加属性和字典转模型

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