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: -----%@  \n  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. 賦值其他


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM