• KVC+Runtime获取类/对象的属性/成员变量/方法/协议并实现字典转模型


    我们知道,KVC+Runtime可以做非常多的事情。有了这个,我们可以实现很多的效果。

    这里来个福利,利用KVC+Runtime获取类/对象的所有成员变量、属性、方法及协议;

    并利用它来实现字典转模型。

    废话不多说,直接上代码:

    1、工具类(其实就是NSObject的一个分类)头文件

     1 #import <Foundation/Foundation.h>
     2 
     3 @interface NSObject (YSRuntime)
     4 
     5 /**
     6  返回当前类的属性数组
     7  
     8  @return 属性数组(如:"name","age","address")
     9  */
    10 + (NSArray *)ys_propertiesList;
    11 
    12 /**
    13  返回当前类的成员变量数组
    14  
    15  @return 成员变量数组
    16  */
    17 + (NSArray *)ys_ivarsList;
    18 
    19 /**
    20  返回当前类的对象方法数组
    21  
    22  @return 方法数组
    23  */
    24 + (NSArray *)ys_methodList;
    25 
    26 /**
    27  返回当前类的协议数组
    28  
    29  @return 协议数组
    30  */
    31 + (NSArray *)ys_protocolList;
    32 
    33 /**
    34  使用字典创建当前类的对象
    35  
    36  @param dictionary 字典
    37  
    38  @return 当前类的对象
    39  */
    40 + (instancetype)ys_objectWithDictionary:(NSDictionary *)dictionary;
    41 
    42 /**
    43  使用字典数组创建当前类的对象数组
    44  
    45  @param dictArray 字典数组
    46  
    47  @return 当前类的对象数组
    48  */
    49 + (NSArray *)ys_objectsWithDictionaryArray:(NSArray<NSDictionary *> *)dictArray;
    50 
    51 @end

    2、下面我们来实现里面的方法,以后就用它来获取类/对象的属性、成员变量、方法和协议

         说明一下:最主要的方法是在objc/runtime.h,这里只是列举了一些,起到抛砖引玉的作用

      1 #import "NSObject+YSRuntime.h"
      2 #import <objc/runtime.h>
      3 
      4 @implementation NSObject (YSRuntime)
      5 
      6 #pragma mark - 属性数组
      7 const char *propertiesKey = "ys.propertiesList";
      8 + (NSArray *)ys_propertiesList {
      9     
     10     // 获取关联对象
     11     NSArray *result = objc_getAssociatedObject(self, propertiesKey);
     12     
     13     if (result != nil) {
     14         return result;
     15     }
     16     
     17     unsigned int count = 0;
     18     objc_property_t *list = class_copyPropertyList([self class], &count);
     19     
     20     NSMutableArray *arrayM = [NSMutableArray array];
     21     
     22     for (unsigned int i = 0; i < count; i++) {
     23         
     24         objc_property_t pty = list[i];
     25         
     26         const char *cName = property_getName(pty);
     27         NSString *name = [NSString stringWithUTF8String:cName];
     28         
     29         [arrayM addObject:name];
     30     }
     31     
     32     free(list);
     33     
     34     // 设置关联对象
     35     objc_setAssociatedObject(self, propertiesKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);
     36     
     37     return objc_getAssociatedObject(self, propertiesKey);
     38 }
     39 
     40 #pragma mark - 私有方法,专门针对字典转模型中的自定义属性,{@"name":@"Dog"},name是属性名,Dog是自定义的模型类,属性名-属性类型
     41 const char *propertiesTypeKey = "ys.propertiesTypeKey";
     42 + (NSArray<NSDictionary *> *)ys_propertiesAndTypeList {
     43     
     44     // 获取关联对象
     45     NSArray *result = objc_getAssociatedObject(self, propertiesTypeKey);
     46     
     47     if (result != nil) {
     48         return result;
     49     }
     50     
     51     unsigned int count = 0;
     52     objc_property_t *list = class_copyPropertyList([self class], &count);
     53     
     54     NSMutableArray<NSDictionary *> *arrayM = [NSMutableArray<NSDictionary *> array];
     55     
     56     for (unsigned int i = 0; i < count; i++) {
     57         
     58         objc_property_t pty = list[i];
     59         
     60         const char *cType = property_getAttributes(pty);
     61         NSString *typeStr = [NSString stringWithUTF8String:cType];
     62         
     63         if([typeStr containsString:@"",&"]){
     64             NSRange typeRange = [typeStr rangeOfString:@"",&"];
     65             NSString *type = [typeStr substringWithRange:NSMakeRange(3, typeRange.location - 3)];
     66             
     67             const char *cName = property_getName(pty);
     68             NSString *name = [NSString stringWithUTF8String:cName];
     69             
     70             NSDictionary *dict = @{name:type};
     71             
     72             [arrayM addObject:dict];
     73         }
     74     }
     75     
     76     free(list);
     77     
     78     // 设置关联对象
     79     objc_setAssociatedObject(self, propertiesTypeKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);
     80     
     81     return objc_getAssociatedObject(self, propertiesTypeKey);
     82 }
     83 
     84 
     85 #pragma mark - 成员变量数组
     86 const char *ivarsKey = "ys.ivarsList";
     87 + (NSArray *)ys_ivarsList {
     88     
     89     // 获取关联对象
     90     NSArray *result = objc_getAssociatedObject(self, ivarsKey);
     91     
     92     if (result != nil) {
     93         return result;
     94     }
     95     
     96     unsigned int count = 0;
     97     Ivar *list = class_copyIvarList([self class], &count);
     98     
     99     NSMutableArray *arrayM = [NSMutableArray array];
    100     
    101     for (unsigned int i = 0; i < count; i++) {
    102         
    103         Ivar ivar = list[i];
    104         
    105         const char *cName = ivar_getName(ivar);
    106         NSString *name = [NSString stringWithUTF8String:cName];
    107         
    108         [arrayM addObject:name];
    109     }
    110     
    111     free(list);
    112     
    113     // 设置关联对象
    114     objc_setAssociatedObject(self, ivarsKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);
    115     
    116     return objc_getAssociatedObject(self, ivarsKey);
    117 }
    118 
    119 #pragma mark - 方法数组
    120 + (NSArray *)ys_methodList {
    121     
    122     unsigned int count = 0;
    123     Method *list = class_copyMethodList([self class], &count);
    124     
    125     NSMutableArray *arrayM = [NSMutableArray array];
    126     
    127     for (unsigned int i = 0; i < count; i++) {
    128         
    129         Method method = list[i];
    130         
    131         SEL selector = method_getName(method);
    132         NSString *name = NSStringFromSelector(selector);
    133         
    134         [arrayM addObject:name];
    135     }
    136     
    137     free(list);
    138     
    139     return arrayM.copy;
    140 }
    141 
    142 #pragma mark - 协议数组
    143 + (NSArray *)ys_protocolList {
    144     
    145     unsigned int count = 0;
    146     __unsafe_unretained Protocol **list = class_copyProtocolList([self class], &count);
    147     
    148     NSMutableArray *arrayM = [NSMutableArray array];
    149     
    150     for (unsigned int i = 0; i < count; i++) {
    151         
    152         Protocol *protocol = list[i];
    153         
    154         const char *cName = protocol_getName(protocol);
    155         NSString *name = [NSString stringWithUTF8String:cName];
    156         
    157         [arrayM addObject:name];
    158     }
    159     
    160     free(list);
    161     
    162     return arrayM.copy;
    163 }
    164 
    165 #pragma mark - 字典 -> 当前类的对象
    166 + (instancetype)ys_objectWithDictionary:(NSDictionary *)dictionary{
    167     
    168     // 当前类的属性数组
    169     NSArray *list = [self ys_propertiesList];
    170     
    171     NSArray<NSDictionary *> *propertyTypeList = [self ys_propertiesAndTypeList];
    172     
    173     id obj = [self new];
    174     
    175     for (NSString *key in dictionary) {
    176         
    177         if([list containsObject:key]){
    178             
    179             if ([dictionary[key] isKindOfClass:[NSDictionary class]]){ // 属性值为字典
    180                 
    181                 for(NSDictionary *dict in propertyTypeList){
    182                     
    183                     if([key isEqualToString: [NSString stringWithFormat:@"%@",dict.allKeys.firstObject]]){
    184                         
    185                         NSString *classTypeStr = dict[key];
    186                         Class class = NSClassFromString(classTypeStr);
    187                         
    188                         id objChild = [class ys_objectWithDictionary:dictionary[key]];
    189                         
    190                         [obj setValue:objChild forKey:key];
    191                     }
    192                 }
    193                 
    194             }
    195             else if([dictionary[key] isKindOfClass:[NSArray<NSDictionary *> class]]){ // 属性值为字典数组
    196                 
    197             }
    198             else{
    199                 [obj setValue:dictionary[key] forKey:key];
    200             }
    201         }
    202     }
    203     
    204     return obj;
    205 }
    206 
    207 #pragma mark - 字典数组 -> 当前类的对象数组
    208 + (NSArray *)ys_objectsWithDictionaryArray:(NSArray<NSDictionary *> *)dictArray {
    209     
    210     if (dictArray.count == 0) {
    211         return nil;
    212     }
    213     
    214     // 当前类的属性数组
    215     NSArray *list = [self ys_propertiesList];
    216     
    217     NSArray<NSDictionary *> *propertyTypeList = [self ys_propertiesAndTypeList];
    218     
    219     NSMutableArray *arrayM = [NSMutableArray array];
    220     for (NSDictionary *dictionary in dictArray) {
    221         
    222         id obj = [self new];
    223         
    224         for (NSString *key in dictionary) {
    225             
    226             if([list containsObject:key]){
    227                 
    228                 if ([dictionary[key] isKindOfClass:[NSDictionary class]]){ // 属性值为字典
    229                     
    230                     for(NSDictionary *dict in propertyTypeList){
    231                         
    232                         if([key isEqualToString: [NSString stringWithFormat:@"%@",dict.allKeys.firstObject]]){
    233                             
    234                             NSString *classTypeStr = dict[key];
    235                             Class class = NSClassFromString(classTypeStr);
    236                             
    237                             id objChild = [class ys_objectWithDictionary:dictionary[key]];
    238                             
    239                             [obj setValue:objChild forKey:key];
    240                         }
    241                     }
    242                     
    243                 }
    244                 else if([dictionary[key] isKindOfClass:[NSArray<NSDictionary *> class]]){ // 属性值为字典数组
    245                     
    246                 }
    247                 else{
    248                     [obj setValue:dictionary[key] forKey:key];
    249                 }
    250             }
    251         }
    252         
    253         [arrayM addObject:obj];
    254     }
    255     
    256     return arrayM.copy;
    257 }
    258 
    259 @end

    3、和YYModel一样,如果对象的一个属性为另一个自定义对象,那么同样可以起到连转的作用;

    4、但比较遗憾的是,也是和YYModel一样,如果有一个属性是数组,又添加了泛型约束,没有取到这个数组的泛型约束。

    我记得,YYModel就有这个问题,因为取不到泛型约束,所以当有属性为数组的时候,需要手动指定数组里面的元素类型。

    希望各位大神看到后不吝赐教。

    5、最后贴出一个小且非常有趣的小东东,里面的key就是我用上面的 “ys_ivarsList” 获取到所有成员变量,然后一级一级找的^_^

    1     // 设置随机颜色给Application的statusBar,从此妈妈再也不用担心statusBar的背景色
    2     id bgStyle = [[UIApplication sharedApplication] valueForKeyPath:@"statusBar.backgroundView.style"];
    3     [bgStyle setValue:[UIColor redColor] forKey:@"backgroundColor"];
    1     // frame = (5 649; 55 13)
    2     // 修改高德地图和logo,删除和替换自己随意定
    3     UIImageView *imgV = [mapView valueForKey:@"logoImageView"]; // 这就就是高德地图的logo
    4     imgV.image = nil; //  我们把imageView的图片置为nil
    5     [mapView setValue:imgV forKey:@"logoImageView"]; // 哈哈,这个高德地图的logo就不见了,妈妈从此再也不需要担心logo了
  • 相关阅读:
    ssh框架构建项目详解--基本概念和struts2
    maven 工程启动找不到 Spring ContextLoaderListener 的解决办法
    ssh(struts,spring,hibernate)开发的初步集成02--xml文件配置
    ssh(struts,spring,hibernate)开发的初步集成01--依赖
    Maven--01(概念)
    hibernate入门知识-03-对象关系映射
    [LeetCode]Construct Binary Tree from Preorder and Inorder Traversal
    [LeetCode]Plus One
    [LeetCode]Triangle
    [LeetCode]PASCAL Triangle系列
  • 原文地址:https://www.cnblogs.com/panda1024/p/6025081.html
Copyright © 2020-2023  润新知