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