zoukankan      html  css  js  c++  java
  • 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. 赋值其他


     
  • 相关阅读:
    微人事项目-mybatis-持久层
    通过外键连接多个表
    springioc
    Redis 消息中间件 ServiceStack.Redis 轻量级
    深度数据对接 链接服务器 数据传输
    sqlserver 抓取所有执行语句 SQL语句分析 死锁 抓取
    sqlserver 索引优化 CPU占用过高 执行分析 服务器检查
    sql server 远程备份 bak 删除
    冒泡排序
    多线程 异步 beginInvoke EndInvoke 使用
  • 原文地址:https://www.cnblogs.com/machao/p/5581037.html
Copyright © 2011-2022 走看看