• YYModel 源码解读(二)之NSObject+YYModel.h (2)


    _YYModelMeta   这个内部的类主要是对这个类的描述。包含了和此类转换相关的数据。

     1 /// A class info in object model.
     2 @interface _YYModelMeta : NSObject {
     3     @package
     4     YYClassInfo *_classInfo;
     5     /// Key:mapped key and key path, Value:_YYModelPropertyMeta.
     6     NSDictionary *_mapper;
     7     /// Array<_YYModelPropertyMeta>, all property meta of this model.
     8     NSArray *_allPropertyMetas;
     9     /// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
    10     NSArray *_keyPathPropertyMetas;
    11     /// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
    12     NSArray *_multiKeysPropertyMetas;
    13     /// The number of mapped key (and key path), same to _mapper.count.
    14     NSUInteger _keyMappedCount;
    15     /// Model class type.
    16     YYEncodingNSType _nsType;
    17     
    18     BOOL _hasCustomWillTransformFromDictionary;
    19     BOOL _hasCustomTransformFromDictionary;
    20     BOOL _hasCustomTransformToDictionary;
    21     BOOL _hasCustomClassFromDictionary;
    22 }
    23 @end
    YYClassInfo *_classInfo; -------------   抽象的类信息
     
    对这个映射的关系 ,可能大家不太了解,下边通过一个例子可以非常清晰的解释这一过程
    自定义的映射关系有一下几种:
    1. 一个属性名对应一个json key

    + (NSDictionary *)modelCustomPropertyMapper {

        return @{@"userID" : @"id",

                 @"idString" : @"idstr",

    }

    2. 多个属性映射同一个json key

    + (NSDictionary *)modelCustomPropertyMapper {

        return @{@"userID" : @"id",

                 @"idString" : @"id",

                 @"uid" : @"id",

    }

    3. 一个属性映射 一个 json keyPath

    + (NSDictionary *)modelCustomPropertyMapper {

        return @{@"userID" : @"user.id",

    }

    4. 一个属性 映射 多个json key 或者 keyPath 

    + (NSDictionary *)modelCustomPropertyMapper {

        // 映射的数组送 包含key 也包含keyPath

        return @{@"userID" : @[@"ID",@"id",@"user.id"],

    }

    上边的第二个情况

    @{@"userID" : @"id",

                 @"idString" : @"id",

                 @"uid" : @"id",

    }

    中为了解决多个属性映射同一 key 的 问题, 引入了链表的 操作

    -> next 指针指向下一个 属性 

    至于 链表的知识 请查资料了解

    NSDictionary *_mapper    ---------- >   来源于原始json 的 映射关系 

     "attitudes_count" = "<_YYModelPropertyMeta: 0x7f8f89efb7d0>";

     "thumbnail" : {
                    "cut_type" : 1,
                    "type" : "WEBP",
                    "url" : "http://ww2.sinaimg.cn/or180/eb8fce65jw1ew468zkxcgj237k1t01l0.jpg",
                    "width" : 266,
                    "height" : 150
                },

    上边的代码是一个json 我们在代码中是这样写的

    @property (nonatomic, strong) YYWeiboPictureMetadata *thumbnail;

    那么 映射完成后的结果是

      "cut_type" = "<_YYModelPropertyMeta: 0x7f8f8eb14c90>";
        height = "<_YYModelPropertyMeta: 0x7f8f8eb14fa0>";
        type = "<_YYModelPropertyMeta: 0x7f8f8eb14b40>";
        url = "<_YYModelPropertyMeta: 0x7f8f8eb14d10>";
        width = "<_YYModelPropertyMeta: 0x7f8f8eb14d90>";

    key 依然是原有的json 的key 但是 value 是 在这个映射类中 包含已经映射成功后的对这个key的YYModelPropertyMeta封装

    NSArray *_allPropertyMetas  --------- >  包含该类和该类的所有父类 直到 NSObject/NSProxy为止的所有属性抽象类NSArray<_YYModelPropertyMeta>

    NSArray *_keyPathPropertyMetas  --------- > 包含 映射为keypath 的 NSArray<_YYModelPropertyMeta>

     

    NSArray *_multiKeysPropertyMetas ----------> @{@"userID" : @[@"ID",@"id",@"user.id"] 类似这样映射,包含有数组映射的NSArray<_YYModelPropertyMeta>

     

    在下边的这个实现方法中不但给上面介绍的 属性赋值外,还给_YYModelPropertyMeta 内的部分属性进行了赋值,代码中都有相信的说明

      1 - (instancetype)initWithClass:(Class)cls {
      2     
      3     // 根据类 生成 抽象的ClassInfo 类
      4     YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
      5     if (!classInfo) return nil;
      6     self = [super init];
      7     
      8     // Get black list
      9     //  黑名单,在转换过程中会忽略数组中属性
     10     NSSet *blacklist = nil;
     11     if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
     12         NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
     13         if (properties) {
     14             blacklist = [NSSet setWithArray:properties];
     15         }
     16     }
     17     
     18     // Get white list
     19     // 白名单,转换过程 中处理 数组内的属性,不处理数组外的数据
     20     NSSet *whitelist = nil;
     21     if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
     22         NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
     23         if (properties) {
     24             whitelist = [NSSet setWithArray:properties];
     25         }
     26     }
     27     
     28     // Get container property's generic class
     29     // 获取 容器内部制定的类型字典
     30     /**
     31      
     32      + (NSDictionary *)modelContainerPropertyGenericClass {
     33      return @{@"shadows" : [Shadow class],
     34      @"borders" : Border.class,
     35      @"attachments" : @"Attachment" };
     36      }
     37      
     38      经过下边转换后得到:
     39      @{
     40      @"shadows" : Shadow,
     41      @"borders" : Border,
     42      @"attachments" : Attachment
     43      };
     44      
     45      */
     46     NSDictionary *genericMapper = nil;
     47     if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
     48         genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
     49         if (genericMapper) {
     50             NSMutableDictionary *tmp = [NSMutableDictionary new];
     51             [genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
     52                 if (![key isKindOfClass:[NSString class]]) return;
     53                 Class meta = object_getClass(obj);
     54                 if (!meta) return;
     55                 if (class_isMetaClass(meta)) {
     56                     tmp[key] = obj;
     57                 } else if ([obj isKindOfClass:[NSString class]]) {
     58                     Class cls = NSClassFromString(obj);
     59                     if (cls) {
     60                         tmp[key] = cls;
     61                     }
     62                 }
     63             }];
     64             genericMapper = tmp;
     65         }
     66     }
     67     
     68     // Create all property metas.
     69     // 获取 所有的属性
     70     NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
     71     YYClassInfo *curClassInfo = classInfo;
     72     
     73     /**
     74      *  向上层便利类,知道父类为空位置,目的是获取所有的属性
     75      */
     76     while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
     77         for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
     78             
     79             //属性名称为空 忽略
     80             if (!propertyInfo.name) continue;
     81             
     82             //在黑名单中 忽略
     83             if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
     84             
     85             // 不在白名单中忽略
     86             if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;
     87             
     88             /**
     89              *  创建对该条属性的抽象类
     90              *  classInfo 
     91              *  propertyInfo
     92              *  genericMapper[propertyInfo.name] 容器内指定的类
     93              */
     94             _YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
     95                                                                     propertyInfo:propertyInfo
     96                                                                          generic:genericMapper[propertyInfo.name]];
     97             
     98             // 判断
     99             if (!meta || !meta->_name) continue;
    100             if (!meta->_getter || !meta->_setter) continue;
    101             
    102             // 如果字典中存在,忽略
    103             if (allPropertyMetas[meta->_name]) continue;
    104             // 给字典复制
    105             allPropertyMetas[meta->_name] = meta;
    106         }
    107         
    108         // 当前的类 指向上一个类的父类
    109         curClassInfo = curClassInfo.superClassInfo;
    110     }
    111     
    112     // 给本类的属性_allPropertyMetas 赋值
    113     if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;
    114     
    115     
    116     // create mapper
    117     NSMutableDictionary *mapper = [NSMutableDictionary new];
    118     NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
    119     NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
    120     
    121     /**
    122      *  如果实现了 modelCustomPropertyMapper 方法
    123      *
    124      *  @param modelCustomPropertyMapper
    125      *
    126      */
    127     if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
    128         
    129         // 获取自定义的字典
    130         NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
    131         // 遍历字典
    132         [customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
    133             
    134             // 根据名字 在 全部属性字典中取出与之相对应的属性抽象类
    135             _YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
    136             if (!propertyMeta) return;
    137             
    138             // 已经找到了结果,可以删除掉,这样在下次查找的时候,就不用做多余的遍历了 ,能够节省时间
    139             [allPropertyMetas removeObjectForKey:propertyName];
    140             
    141             if ([mappedToKey isKindOfClass:[NSString class]]) {
    142                 if (mappedToKey.length == 0) return;
    143                 
    144                 // 给抽象类的_mappedToKey 赋值 标示要被映射的名称 下边的指的就是@"n",@"p"...
    145                 /*
    146                  + (NSDictionary *)modelCustomPropertyMapper {
    147                  return @{@"name" : @"n",
    148                  @"page" : @"p",
    149                  @"desc" : @"ext.desc",
    150                  @"bookID" : @[@"id",@"ID",@"book_id"]};
    151                  }
    152                  */
    153                 propertyMeta->_mappedToKey = mappedToKey;
    154                 
    155                 // 映射对象 如果是keypath ,@"user.id"
    156                 NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];
    157                 
    158                 // 遍历数组 ,删除空字符串
    159                 for (NSString *onePath in keyPath) {
    160                     
    161                     // 如果存在空字符 则在原数组中删除
    162                     if (onePath.length == 0) {
    163                         NSMutableArray *tmp = keyPath.mutableCopy;
    164                         [tmp removeObject:@""];
    165                         keyPath = tmp;
    166                         break;
    167                     }
    168                 }
    169                 // keypath 的个数大于1 说明为 有效路径
    170                 if (keyPath.count > 1) {
    171                     
    172                     // 赋值
    173                     propertyMeta->_mappedToKeyPath = keyPath;
    174                     [keyPathPropertyMetas addObject:propertyMeta];
    175                 }
    176                 
    177                 // 控制 propertyMeta 的 next 指针 指向下一个 映射
    178                 propertyMeta->_next = mapper[mappedToKey] ?: nil;
    179                 mapper[mappedToKey] = propertyMeta;
    180                 
    181             } else if ([mappedToKey isKindOfClass:[NSArray class]]) {
    182                 
    183                 NSMutableArray *mappedToKeyArray = [NSMutableArray new];
    184                 for (NSString *oneKey in ((NSArray *)mappedToKey)) {
    185                     if (![oneKey isKindOfClass:[NSString class]]) continue;
    186                     if (oneKey.length == 0) continue;
    187                     
    188                     // 如果映射的是数组,保存 数组到mappedToKeyArray 中, 否则保存 映射字符串
    189                     NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
    190                     if (keyPath.count > 1) {
    191                         [mappedToKeyArray addObject:keyPath];
    192                     } else {
    193                         [mappedToKeyArray addObject:oneKey];
    194                     }
    195                     
    196                     // 赋值
    197                     if (!propertyMeta->_mappedToKey) {
    198                         propertyMeta->_mappedToKey = oneKey;
    199                         propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
    200                     }
    201                 }
    202                 
    203                 if (!propertyMeta->_mappedToKey) return;
    204                 
    205                 propertyMeta->_mappedToKeyArray = mappedToKeyArray;
    206                 [multiKeysPropertyMetas addObject:propertyMeta];
    207                 
    208                 propertyMeta->_next = mapper[mappedToKey] ?: nil;
    209                 mapper[mappedToKey] = propertyMeta;
    210             }
    211         }];
    212     }
    213     
    214     [allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
    215         propertyMeta->_mappedToKey = name;
    216         propertyMeta->_next = mapper[name] ?: nil;
    217         mapper[name] = propertyMeta;
    218     }];
    219     
    220     if (mapper.count) _mapper = mapper;
    221     if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
    222     if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
    223     
    224     NSLog(@" allmapper: -----%@  
      keyPathPropertyMetas: ------%@",allPropertyMetas,keyPathPropertyMetas);
    225     _classInfo = classInfo;
    226     _keyMappedCount = _allPropertyMetas.count;
    227     _nsType = YYClassGetNSType(cls);
    228     _hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
    229     _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
    230     _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
    231     _hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
    232     
    233     return self;
    234 }

    下边的方法是 在缓存中读取 抽象类

     1 /// Returns the cached model class meta
     2 + (instancetype)metaWithClass:(Class)cls {
     3     if (!cls) return nil;
     4     static CFMutableDictionaryRef cache;
     5     static dispatch_once_t onceToken;
     6     static dispatch_semaphore_t lock;
     7     dispatch_once(&onceToken, ^{
     8         cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
     9         lock = dispatch_semaphore_create(1);
    10     });
    11     dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
    12     _YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
    13     dispatch_semaphore_signal(lock);
    14     if (!meta || meta->_classInfo.needUpdate) {
    15         meta = [[_YYModelMeta alloc] initWithClass:cls];
    16         if (meta) {
    17             dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
    18             CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
    19             dispatch_semaphore_signal(lock);
    20         }
    21     }
    22     return meta;
    23 }
    简单总结下_YYModelMeta 实现的思路

    1.根据类 生成 抽象的ClassInfo 类     

    classInfo

    2. 获取黑名单,在转换过程中会忽略数组中属性

    blacklist

    3. 获取白名单,转换过程 中处理 数组内的属性,不处理数组外的数据

    whitelist

    4.对实现了modelContainerPropertyGenericClass 方法 进行必要的转换 类中包含有容易的情况

      /**
         
         + (NSDictionary *)modelContainerPropertyGenericClass {
         return @{@"shadows" : [Shadow class],
         @"borders" : Border.class,
         @"attachments" : @"Attachment" };
         }
         
         经过下边转换后得到:
         @{
         @"shadows" : Shadow,
         @"borders" : Border,
         @"attachments" : Attachment
         };
         
         */

    5. 获取 所有的属性 

    allPropertyMetas

    /**
         *  向上层便利类,知道父类为空位置,目的是获取所有的属性
         */

    6. 给本类的属性_allPropertyMetas 赋值

    if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;

    7. 如果实现了 modelCustomPropertyMapper 方法 也就是自定义了映射

       7.1 通过下边的方法可判断是不是进行了自定义映射

    if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)])

       7.2 获取自定义的字典  customMapper

    NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];

       7.3 遍历 字典

             mappedToKey  有两个类型 

             一种是:字符串, 另一种是 字符数组

                   如果是字符串 propertyMeta->_mappedToKey = mappedToKey 直接赋值

                   如果是数组 取数组中第一个不为空的字符串

                   同理,keypath 也同上一样的获取到

     

                    multiKeysPropertyMetas 同在 在是数组的情况加 添加抽象类

     

        8. 赋值其他


     
  • 相关阅读:
    将Linux下python默认版本切换成替代版本
    ubuntu下卸载python2和升级python3.5
    Linux下安装theano
    梯度下降法
    使用Matlab实现对图片的缩放
    matlab 中的删除文件
    解决aws ec2的centos7设置时区无效
    yum安装redis5/mq/consul
    django web应用runserver模式下cpu占用高解决办法
    N1如何完美刷入armbian系统教程
  • 原文地址:https://www.cnblogs.com/machao/p/5581037.html
Copyright © 2020-2023  润新知