_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. 賦值其他