YYModel是一款非常好用且非常轻量级的JSON模型转换库,源码一共就五个文件,去掉声明文件,所有的实现逻辑都在NSObject+YYModel.m
和YYClassInfo.m
这两个文件中,如图:
YYClassInfo功能主要是将Runtime层级中的一些api(结构体封)装到NSObject子类中,将runtime的api对象化,方便调用;NSObject+YYModel是提供调用的接口以及实现具体的模型转换逻辑,YYModel的代码结构如图:
1.1、YYClassIvarInfo
对objc_ivar
封装对比
YYClassIvarInfo
对象声明:@interface YYClassIvarInfo : NSObject @property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct @property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name @property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding @property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type - (instancetype)initWithIvar:(Ivar)ivar; @end 复制代码
objc_ivar
的定义:struct objc_ivar { char * _Nullable ivar_name OBJC2_UNAVAILABLE; // 变量名称 char * _Nullable ivar_type OBJC2_UNAVAILABLE; // 变量类型 int ivar_offset OBJC2_UNAVAILABLE; // 变量偏移量 #ifdef __LP64__ // 如果已定义 __LP64__ 则表示正在构建 64 位目标 int space OBJC2_UNAVAILABLE; // 变量空间 #endif } 复制代码
1.2、YYClassMethodInfo
对objc_method
封装对比
YYClassMethodInfo
对象声明:
@interface YYClassMethodInfo : NSObject @property (nonatomic, assign, readonly) Method method; ///< 方法 @property (nonatomic, strong, readonly) NSString *name; ///< 方法名称 @property (nonatomic, assign, readonly) SEL sel; ///< 方法选择器 @property (nonatomic, assign, readonly) IMP imp; ///< 方法实现,指向实现方法函数的函数指针 @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< 方法参数和返回类型编码 @property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< 返回值类型编码 @property (nullable, nonatomic, strong, readonly) NSArray<nsstring *> *argumentTypeEncodings; ///< 参数类型编码数组 - (instancetype)initWithMethod:(Method)method; @end 复制代码
Runtime中objc_method
的定义:
struct objc_method { SEL _Nonnull method_name OBJC2_UNAVAILABLE; // 方法名称 char * _Nullable method_types OBJC2_UNAVAILABLE; // 方法类型 IMP _Nonnull method_imp OBJC2_UNAVAILABLE; // 方法实现(函数指针) } 复制代码
1.3、YYClassPropertyInfo
对property_t
封装对比
YYClassPropertyInfo
对象声明:
@interface YYClassPropertyInfo : NSObject @property (nonatomic, assign, readonly) objc_property_t property; ///< 属性 @property (nonatomic, strong, readonly) NSString *name; ///< 属性名称 @property (nonatomic, assign, readonly) YYEncodingType type; ///< 属性类型 @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< 属性类型编码 @property (nonatomic, strong, readonly) NSString *ivarName; ///< 变量名称 @property (nullable, nonatomic, assign, readonly) Class cls; ///< 类型 @property (nullable, nonatomic, strong, readonly) NSArray<nsstring *> *protocols; ///< 属性相关协议 @property (nonatomic, assign, readonly) SEL getter; ///< getter 方法选择器 @property (nonatomic, assign, readonly) SEL setter; ///< setter 方法选择器 - (instancetype)initWithProperty:(objc_property_t)property; 复制代码
Runtime中property_t
的定义:
struct property_t { const char *name; // 名称 const char *attributes; // 修饰 }; 复制代码
1.4、YYClassInfo
对objc_class
封装对比
YYClassInfo
对象声明:@interface YYClassInfo : NSObject @property (nonatomic, assign, readonly) Class cls; ///< 类 @property (nullable, nonatomic, assign, readonly) Class superCls; ///< 超类 @property (nullable, nonatomic, assign, readonly) Class metaCls; ///< 元类 @property (nonatomic, readonly) BOOL isMeta; ///< 元类标识,自身是否为元类 @property (nonatomic, strong, readonly) NSString *name; ///< 类名称 @property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< 父类(超类)信息 @property (nullable, nonatomic, strong, readonly) NSDictionary<nsstring *, yyclassivarinfo *> *ivarInfos; ///< 变量信息 @property (nullable, nonatomic, strong, readonly) NSDictionary<nsstring *, yyclassmethodinfo *> *methodInfos; ///< 方法信息 @property (nullable, nonatomic, strong, readonly) NSDictionary<nsstring *, yyclasspropertyinfo *> *propertyInfos; ///< 属性信息 - (void)setNeedUpdate; - (BOOL)needUpdate; + (nullable instancetype)classInfoWithClass:(Class)cls; + (nullable instancetype)classInfoWithClassName:(NSString *)className; @end 复制代码
objc_class
的定义:// objc.h typedef struct objc_class *Class; // runtime.h struct objc_class { Class _Nonnull isa OBJC_ISA_AVAILABILITY; // isa 指针 #if !__OBJC2__ Class _Nullable super_class OBJC2_UNAVAILABLE; // 父类(超类)指针 const char * _Nonnull name OBJC2_UNAVAILABLE; // 类名 long version OBJC2_UNAVAILABLE; // 版本 long info OBJC2_UNAVAILABLE; // 信息 long instance_size OBJC2_UNAVAILABLE; // 初始尺寸 struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; // 变量列表 struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; // 方法列表 struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; // 缓存 struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; // 协议列表 #endif } OBJC2_UNAVAILABLE; 复制代码
_YYModelPropertyMeta
对象的声明定义:@interface _YYModelPropertyMeta : NSObject { @package NSString *_name; ///< 属性名称 YYEncodingType _type; ///< 属性类型 YYEncodingNSType _nsType; ///< 属性在 Foundation 框架中的类型 BOOL _isCNumber; ///< 是否为 CNumber Class _cls; ///< 属性类 Class _genericCls; ///< 属性包含的泛型类型,没有则为 nil SEL _getter; ///< getter SEL _setter; ///< setter BOOL _isKVCCompatible; ///< 如果可以使用 KVC 则返回 YES BOOL _isStructAvailableForKeyedArchiver; ///< 如果可以使用 archiver/unarchiver 归/解档则返回 YES BOOL _hasCustomClassFromDictionary; ///< 类/泛型自定义类型,例如需要在数组中实现不同类型的转换需要用到 /* property->key: _mappedToKey:key _mappedToKeyPath:nil _mappedToKeyArray:nil property->keyPath: _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil property->keys: _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath _mappedToKeyArray:keys(array) */ NSString *_mappedToKey; ///< 映射 key NSArray *_mappedToKeyPath; ///< 映射 keyPath,如果没有映射到 keyPath 则返回 nil NSArray *_mappedToKeyArray; ///< key 或者 keyPath 的数组,如果没有映射多个键的话则返回 nil YYClassPropertyInfo *_info; ///< 属性信息,详见上文 YYClassPropertyInfo && property_t 章节 _YYModelPropertyMeta *_next; ///< 如果有多个属性映射到同一个 key 则指向下一个模型属性元 } @end 复制代码
_YYModelMeta
对象的声明定义:@interface _YYModelMeta : NSObject { @package YYClassInfo *_classInfo; /// Key:被映射的 key 与 keyPath, Value:_YYModelPropertyMeta. NSDictionary *_mapper; /// Array<_YYModelPropertyMeta>, 当前模型的所有 _YYModelPropertyMeta 数组 NSArray *_allPropertyMetas; /// Array<_YYModelPropertyMeta>, 被映射到 keyPath 的 _YYModelPropertyMeta 数组 NSArray *_keyPathPropertyMetas; /// Array<_YYModelPropertyMeta>, 被映射到多个 key 的 _YYModelPropertyMeta 数组 NSArray *_multiKeysPropertyMetas; /// 映射 key 与 keyPath 的数量,等同于 _mapper.count NSUInteger _keyMappedCount; /// 模型 class 类型 YYEncodingNSType _nsType; ///作用:判断YYModel一系列协议方法是否实现 BOOL _hasCustomWillTransformFromDictionary;//解析前是否需要更改字典 BOOL _hasCustomTransformFromDictionary;//字典转模型后是否需要补充处理 BOOL _hasCustomTransformToDictionary;//模型转字典后是否需要补充处理 BOOL _hasCustomClassFromDictionary;//是否需要根据dic的内容转换为不同类型的模型 } @end 复制代码
+ (nullable instancetype)yy_modelWithJSON:(id)json;
,内部调用2;+ (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary;
,内部调用4;LGModel *model = [LGModel yy_modelWithDictionary:dict]; 复制代码
- (BOOL)yy_modelSetWithJSON:(id)json;
,内部调用4;- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic;
,json转model的最终实现方法;LGModel *model1 = [[LGModel alloc]init]; [model1 yy_modelSetWithDictionary:dict]; 复制代码
- (nullable id)yy_modelToJSONObject;
,model数据转换为JSON数据,最终实现- (nullable NSData *)yy_modelToJSONData;
,内部调用1- (nullable NSString *)yy_modelToJSONString;
,内部调用1- (nullable id)yy_modelCopy;
- (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder;
- (id)yy_modelInitWithCoder:(NSCoder *)aDecoder;
- (NSUInteger)yy_modelHash;
- (BOOL)yy_modelIsEqual:(id)model;
- (NSString *)yy_modelDescription;
/** Creates and returns an array from a json-array. This method is thread-safe. @param cls The instance's class in array. @param json A json array of `NSArray`, `NSString` or `NSData`. Example: [{"name","Mary"},{name:"Joe"}] @return A array, or nil if an error occurs. */ + (nullable NSArray *)yy_modelArrayWithClass:(Class)cls json:(id)json; 复制代码
/** Creates and returns a dictionary from a json. This method is thread-safe. @param cls The value instance's class in dictionary. @param json A json dictionary of `NSDictionary`, `NSString` or `NSData`. Example: {"user1":{"name","Mary"}, "user2": {name:"Joe"}} @return A dictionary, or nil if an error occurs. */ + (nullable NSDictionary *)yy_modelDictionaryWithClass:(Class)cls json:(id)json; 复制代码
+ (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;
,//返回一个 Dict,将 Model 属性名对映射到 JSON 的 Key。 + (NSDictionary *)modelCustomPropertyMapper { return @{@"messageId":@[@"id",@"ID",@"book_id"]}; } 复制代码
json key (key path)
映射到一个或多个属性。如果一个属性没有映射关系,那默认会使用相同属性名作为映射。+ (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
// 返回容器类中的所需要存放的数据类型 (以 Class 或 Class Name 的形式)。 + (NSDictionary *)modelContainerPropertyGenericClass{ return @{@"books" : LGSubModel.class, @"infoDict" : [LGPerson class], @"likedUserIds" : @"NSNumber" }; } 复制代码
[LGPerson class]
,LGPerson.class
,@"LGPerson"
没有明显的区别。+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
@implementation LGPerson +(Class)modelCustomClassForDictionary:(NSDictionary *)dictionary { if ([dictionary[@"gender"] integerValue] == 1) { return LGMan.class; } return self; } @end 复制代码
+ (nullable NSArray<NSString *> *)modelPropertyBlacklist;
+ (nullable NSArray<NSString *> *)modelPropertyWhitelist;
// 如果实现了该方法,则处理过程中会忽略该列表内的所有属性 +(NSArray<NSString *> *)modelPropertyBlacklist { return @[@"subject"]; } // 如果实现了该方法,则处理过程中不会处理该列表外的属性 + (NSArray<NSString *> *)modelPropertyWhitelist { return @[@"name",@"age",@"num"]; } 复制代码
- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
,发生在字典转模型之前,最后对网络字典做一次处理;- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic{ if ([dic[@"gender"] integerValue] == 1) { return nil;//不接受男性 } return dic; } 复制代码
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
,YYModel无法处理或处理后格式类型等不正确,可以在这里重新赋值处理;// 当 JSON 转为 Model 完成后,该方法会被调用。 // 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。 // 你也可以在这里做一些自动转换不能完成的工作。 - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic{ NSNumber *interval = dic[@"timeInterval"]; if (![interval isKindOfClass:[NSNumber class]]) { return NO; } _createTime = [NSDate dateWithTimeIntervalSince1970:[interval floatValue]]; return YES; } 复制代码
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
,同样,转为json时一样有格式或类型不正确,可以在这里重新赋值处理;// 当 Model 转为 JSON 完成后,该方法会被调用。 // 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。 // 你也可以在这里做一些自动转换不能完成的工作。 - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic { if (!_createTime) { return NO; } dic[@"timeInterval"] = @([_createTime timeIntervalSince1970]) ; return YES; } 复制代码
YYModel调用逻辑流程图:
yy_modelWithJSON
作为JSON模型转换的入口,将传入的对象转换成字典,调用yy_modelWithDictionary:
,这个方法的内部实现如下:+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary { if (!dictionary || dictionary == (id)kCFNull) return nil; if (![dictionary isKindOfClass:[NSDictionary class]]) return nil; /// 获取当前模型类 Class cls = [self class]; ///2、通过class 获取到各种信息,然后封装到_YYModelMeta中 _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls]; ///是否需要根据字典内容修改模型类 if (modelMeta->_hasCustomClassFromDictionary) { cls = [cls modelCustomClassForDictionary:dictionary] ?: cls; } ///模型类实例化 NSObject *one = [cls new]; ///3、实现JSON转模型功能 if ([one yy_modelSetWithDictionary:dictionary]) return one; return nil; } 复制代码
metaWithClass:
通过class 获取到各种信息,然后封装到_YYModelMeta中,返回缓存的_YYModelMeta信息,实现如下:/// Returns the cached model class meta + (instancetype)metaWithClass:(Class)cls { if (!cls) return nil; ///声明缓存模型类和类信息的字典,key为类名 static CFMutableDictionaryRef cache; static dispatch_once_t onceToken; ///保证线程安全锁的声明 static dispatch_semaphore_t lock; dispatch_once(&onceToken, ^{///保证缓存字典只实例化一次 ///实例化缓存字典 cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); ///锁创建 lock = dispatch_semaphore_create(1); }); dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);///锁启用 ///以类名为key从缓存取类信息_YYModelMeta _YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls)); dispatch_semaphore_signal(lock);///锁关闭 if (!meta || meta->_classInfo.needUpdate) { ///2.1、缓存未取到类信息meta,根据model类cls去实例化该类信息 meta = [[_YYModelMeta alloc] initWithClass:cls]; if (meta) { dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);///锁启用 ///缓存类信息meta,类名为key CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta)); dispatch_semaphore_signal(lock);///锁关闭 } } return meta; } 复制代码
initWithClass:
方法有点长,其实现如下:- (instancetype)initWithClass:(Class)cls { ///2.1.1、从Class中获取类信息,并封装成对象 YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls]; if (!classInfo) return nil; self = [super init]; // Get black list-获取黑名单 NSSet *blacklist = nil; if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) { NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist]; if (properties) { blacklist = [NSSet setWithArray:properties]; } } // Get white list-获取白名单 NSSet *whitelist = nil; if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) { NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist]; if (properties) { whitelist = [NSSet setWithArray:properties]; } } // Get container property’s generic class - 返回容器类中的所需要存放的数据类型 NSDictionary *genericMapper = nil; if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) { genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass]; if (genericMapper) { NSMutableDictionary *tmp = [NSMutableDictionary new]; [genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {/// key是字符串,obj是容器中存放的实例的类对象 if (![key isKindOfClass:[NSString class]]) return;//key必须是字符串类型 ///获取容器中存放对象的类的元类 Class meta = object_getClass(obj); if (!meta) return; if (class_isMetaClass(meta)) { //处理[LGPerson class],LGPerson.class 两种类型 tmp[key] = obj; } else if ([obj isKindOfClass:[NSString class]]) { //处理@"LGPerson"类型 Class cls = NSClassFromString(obj); if (cls) { tmp[key] = cls; } } }]; genericMapper = tmp;//容器类属性以及对应Class 的字典 } } // Create all property metas.-获取所有要解析的属性,包括排除黑名单、验证白名单、验证是否有getter 和setter 等 NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new]; YYClassInfo *curClassInfo = classInfo; while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)-递归解析父类,但忽略根类 for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) { if (!propertyInfo.name) continue; if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;//在黑名单内不处理 if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;//不在白名单内不处理 ///2.1.2、实例化_YYModelPropertyMeta _YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo propertyInfo:propertyInfo generic:genericMapper[propertyInfo.name]]; if (!meta || !meta->_name) continue; if (!meta->_getter || !meta->_setter) continue; if (allPropertyMetas[meta->_name]) continue; allPropertyMetas[meta->_name] = meta; } curClassInfo = curClassInfo.superClassInfo; }///得到所有要解析的属性信息集合 if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy; // create mapper NSMutableDictionary *mapper = [NSMutableDictionary new]; NSMutableArray *keyPathPropertyMetas = [NSMutableArray new]; NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new]; ///属性名和json中的键不一样的,为属性设置json中的key或者keyPath if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) { NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper]; [customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) { _YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName]; //判断属性名对应的_YYModelPropertyMeta实例是否存在 if (!propertyMeta) return; //移除属性名对应的_YYModelPropertyMeta实例 [allPropertyMetas removeObjectForKey:propertyName]; //处理属性名和json中的键一一对应的情况 if ([mappedToKey isKindOfClass:[NSString class]]) { if (mappedToKey.length == 0) return; propertyMeta->_mappedToKey = mappedToKey; NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."]; for (NSString *onePath in keyPath) { if (onePath.length == 0) { NSMutableArray *tmp = keyPath.mutableCopy; [tmp removeObject:@""]; keyPath = tmp; break; } } if (keyPath.count > 1) { propertyMeta->_mappedToKeyPath = keyPath; [keyPathPropertyMetas addObject:propertyMeta]; } propertyMeta->_next = mapper[mappedToKey] ?: nil; mapper[mappedToKey] = propertyMeta;//为json中的键赋值 ///处理属性名对应json中的多个键 } else if ([mappedToKey isKindOfClass:[NSArray class]]) { NSMutableArray *mappedToKeyArray = [NSMutableArray new]; for (NSString *oneKey in ((NSArray *)mappedToKey)) { if (![oneKey isKindOfClass:[NSString class]]) continue; if (oneKey.length == 0) continue; NSArray *keyPath = [oneKey componentsSeparatedByString:@"."]; if (keyPath.count > 1) { [mappedToKeyArray addObject:keyPath]; } else { [mappedToKeyArray addObject:oneKey]; } if (!propertyMeta->_mappedToKey) { propertyMeta->_mappedToKey = oneKey; propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil; } } if (!propertyMeta->_mappedToKey) return; propertyMeta->_mappedToKeyArray = mappedToKeyArray; [multiKeysPropertyMetas addObject:propertyMeta]; propertyMeta->_next = mapper[mappedToKey] ?: nil; mapper[mappedToKey] = propertyMeta;//为json中的键赋值 } }]; } ///将allPropertyMetas中剩下的值添加到mapper中 [allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) { propertyMeta->_mappedToKey = name; propertyMeta->_next = mapper[name] ?: nil; mapper[name] = propertyMeta; }]; if (mapper.count) _mapper = mapper;//属性赋值 if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;//属性赋值 if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;//属性赋值 _classInfo = classInfo;//属性赋值 _keyMappedCount = _allPropertyMetas.count;//属性赋值 _nsType = YYClassGetNSType(cls);//属性赋值 _hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);//解析前更改字典方法是否实现 _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);//字典转模型后补充处理方法是否实现 _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);//模型转字典后补充处理方法是否实现 _hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);//根据dic的内容转换为不同类型的模型方法是否实现 return self; } 复制代码
YYClassInfo
对象YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
+ (instancetype)classInfoWithClass:(Class)cls { if (!cls) return nil; static CFMutableDictionaryRef classCache;//声明类信息缓存字典 static CFMutableDictionaryRef metaCache;//声明元类信息缓存字典 static dispatch_once_t onceToken; static dispatch_semaphore_t lock;//声明安全锁 dispatch_once(&onceToken, ^{ classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);//类信息缓存d字典实例化 metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);//元类信息缓存字典实例化 lock = dispatch_semaphore_create(1); }); dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); ///根据类名,从缓存获取类/元类信息 YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls)); if (info && info->_needUpdate) { //2.1.1.1 更新类信息 [info _update]; } dispatch_semaphore_signal(lock); if (!info) { //2.1.1.2缓存中没有获取到类信息,根据cls,初始化类信息 info = [[YYClassInfo alloc] initWithClass:cls]; if (info) { dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); ///缓存到字典 CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info)); dispatch_semaphore_signal(lock); } } return info; } 复制代码
_update
方法- (void)_update { ///清空原有的成员列表,方法,属性数组 _ivarInfos = nil; _methodInfos = nil; _propertyInfos = nil; Class cls = self.cls; //获取方法列表 unsigned int methodCount = 0; Method *methods = class_copyMethodList(cls, &methodCount); if (methods) { NSMutableDictionary *methodInfos = [NSMutableDictionary new]; _methodInfos = methodInfos; for (unsigned int i = 0; i < methodCount; i++) { //对Method 做了一层封装,封装成了YYClassMethodInfo YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]]; if (info.name) methodInfos[info.name] = info; } free(methods); } // 3.获取属性列表 unsigned int propertyCount = 0; objc_property_t *properties = class_copyPropertyList(cls, &propertyCount); if (properties) { NSMutableDictionary *propertyInfos = [NSMutableDictionary new]; _propertyInfos = propertyInfos; for (unsigned int i = 0; i < propertyCount; i++) { // 对Property做了一层封装,封装成了YYClassPropertyInfo YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]]; if (info.name) propertyInfos[info.name] = info; } free(properties); } // 4.获取示例变量列表 unsigned int ivarCount = 0; Ivar *ivars = class_copyIvarList(cls, &ivarCount); if (ivars) { NSMutableDictionary *ivarInfos = [NSMutableDictionary new]; _ivarInfos = ivarInfos; for (unsigned int i = 0; i < ivarCount; i++) { // 对成员变量做了一层封装,封装成了YYClassIvarInfo YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]]; if (info.name) ivarInfos[info.name] = info; } free(ivars); } if (!_ivarInfos) _ivarInfos = @{}; if (!_methodInfos) _methodInfos = @{}; if (!_propertyInfos) _propertyInfos = @{}; _needUpdate = NO; } 复制代码
initWithClass:
方法;- (instancetype)initWithClass:(Class)cls { if (!cls) return nil; self = [super init]; _cls = cls; _superCls = class_getSuperclass(cls); _isMeta = class_isMetaClass(cls); if (!_isMeta) { _metaCls = objc_getMetaClass(class_getName(cls)); } _name = NSStringFromClass(cls); [self _update]; _superClassInfo = [self.class classInfoWithClass:_superCls]; return self; } 复制代码
_YYModelPropertyMeta
的+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic
方法+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic { // support pseudo generic class with protocol name if (!generic && propertyInfo.protocols) { for (NSString *protocol in propertyInfo.protocols) { Class cls = objc_getClass(protocol.UTF8String); if (cls) { generic = cls; break; } } } _YYModelPropertyMeta *meta = [self new]; meta->_name = propertyInfo.name; meta->_type = propertyInfo.type; meta->_info = propertyInfo; meta->_genericCls = generic; if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) { meta->_nsType = YYClassGetNSType(propertyInfo.cls); } else { meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type); } if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) { /* It seems that NSKeyedUnarchiver cannot decode NSValue except these structs: */ static NSSet *types = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSMutableSet *set = [NSMutableSet new]; // 32 bit [set addObject:@"{CGSize=ff}"]; [set addObject:@"{CGPoint=ff}"]; [set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"]; [set addObject:@"{CGAffineTransform=ffffff}"]; [set addObject:@"{UIEdgeInsets=ffff}"]; [set addObject:@"{UIOffset=ff}"]; // 64 bit [set addObject:@"{CGSize=dd}"]; [set addObject:@"{CGPoint=dd}"]; [set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"]; [set addObject:@"{CGAffineTransform=dddddd}"]; [set addObject:@"{UIEdgeInsets=dddd}"]; [set addObject:@"{UIOffset=dd}"]; types = set; }); if ([types containsObject:propertyInfo.typeEncoding]) { meta->_isStructAvailableForKeyedArchiver = YES; } } meta->_cls = propertyInfo.cls; if (generic) { meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)]; } else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) { meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)]; } if (propertyInfo.getter) { if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) { meta->_getter = propertyInfo.getter; } } if (propertyInfo.setter) { if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) { meta->_setter = propertyInfo.setter; } } if (meta->_getter && meta->_setter) { /* KVC invalid type: KVC失效类型:长整型 双精度 指针(SEL/CF对象) long double pointer (such as SEL/CoreFoundation object) */ switch (meta->_type & YYEncodingTypeMask) { case YYEncodingTypeBool: case YYEncodingTypeInt8: case YYEncodingTypeUInt8: case YYEncodingTypeInt16: case YYEncodingTypeUInt16: case YYEncodingTypeInt32: case YYEncodingTypeUInt32: case YYEncodingTypeInt64: case YYEncodingTypeUInt64: case YYEncodingTypeFloat: case YYEncodingTypeDouble: case YYEncodingTypeObject: case YYEncodingTypeClass: case YYEncodingTypeBlock: case YYEncodingTypeStruct: case YYEncodingTypeUnion: { meta->_isKVCCompatible = YES; } break; default: break; } } return meta; } 复制代码
yy_modelSetWithDictionary:
,实现如下:- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic { if (!dic || dic == (id)kCFNull) return NO; if (![dic isKindOfClass:[NSDictionary class]]) return NO; // 获取类信息,这里如果使用的是原来的类,则其实是从缓存中取出来的,因为在前面已经调用过metaWithClass方法了。如果是设置了转换的类,则可能会再重新完整执行一次metaWithClass。 _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)]; if (modelMeta->_keyMappedCount == 0) return NO; //解析前是否需要更改字典 if (modelMeta->_hasCustomWillTransformFromDictionary) { dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic]; if (![dic isKindOfClass:[NSDictionary class]]) return NO; } // 2.自定义的一个context 结构体,把model 的信息、model 对象指针、以及参数字典赋值上 ModelSetContext context = {0}; context.modelMeta = (__bridge void *)(modelMeta); context.model = (__bridge void *)(self); context.dictionary = (__bridge void *)(dic); if (modelMeta-> >= CFDictionaryGetCount((CFDictionaryRef)dic)) { CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context); if (modelMeta->_keyPathPropertyMetas) { CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas, CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)), ModelSetWithPropertyMetaArrayFunction, &context); } if (modelMeta->_multiKeysPropertyMetas) { CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas, CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)), ModelSetWithPropertyMetaArrayFunction, &context); } } else {//如果转换属性个数小于字典里个键值对个数 CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas, CFRangeMake(0, modelMeta->_keyMappedCount), ModelSetWithPropertyMetaArrayFunction, &context); } // 最后,如果有一些特殊的属性,需要自己转换赋值的话,再处理一下 if (modelMeta->_hasCustomTransformFromDictionary) { return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic]; } return YES; } 复制代码
CFDictionaryApplyFunction
和CFArrayApplyFunction
总体概述上面真正起到遍历赋值的方法是CFDictionaryApplyFunction
和CFArrayApplyFunction
,通过这两个CoreFoundation的方法来遍历传入的字典来给模型类赋值。这两个方法和OC自带的遍历方法相比,会带来不少性能上的提升,缺点就是写起来相当麻烦。这两个方法都有一个回调函数(ModelSetWithDictionaryFunction
和ModelSetWithPropertyMetaArrayFunction
),在遍历字典时,将字典的每一个value赋值给对应的模型类中的对应属性。
CFDictionaryApplyFunction
和CFArrayApplyFunction
在官方文档里有解释:// Calls a function once for each key-value pair in a dictionary. // 对于字典里的每一个键值对,都会调用一次applier 方法 void CFDictionaryApplyFunction(CFDictionaryRef theDict, CFDictionaryApplierFunction applier, void *context); 复制代码
//Calls a function once for each element in range in an array。 // 对于数组中指定range返回的每一个元素调用一次applier void CFArrayApplyFunction(CFArrayRef theArray, CFRange range, CFArrayApplierFunction applier, void *context); 复制代码
YYModel中对两个回调函数的实现如下:
CFDictionaryApplyFunction
的回调函数ModelSetWithDictionaryFunction
的实现:static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) { ModelSetContext *context = _context; // 1.从上下文中取到model 的信息 __unsafe_unretained _YYModelMeta meta = (__bridge _YYModelMeta )(context->modelMeta); // 2.从转换字典中取到属性对象 __unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)]; __unsafe_unretained id model = (__bridge id)(context->model); // 3.以防有多个相同key 的不同值 while (propertyMeta) { if (propertyMeta->_setter) { // 为model 的该属性赋值。 ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta); } propertyMeta = propertyMeta->_next; }; } 复制代码
而ModelSetValueForProperty
方法中会根据属性的类型调用objc_msgSend
来赋相应类型的值。例如字符串类型的赋值:
if (meta->_nsType == YYEncodingTypeNSString) { ((void ()(id, SEL, id))(void ) objc_msgSend)((id)model, meta->_setter, value); } 复制代码
CFArrayApplyFunction
的回调函数ModelSetWithPropertyMetaArrayFunction
与字典的处理方式类似,只不过applier 中的参数直接就是属性对象罢了。static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) { ModelSetContext *context = _context; // 获取字典参数 __unsafe_unretained NSDictionary dictionary = (__bridge NSDictionary )(context->dictionary); // 这里只是强转一下类型而已 __unsafe_unretained _YYModelPropertyMeta propertyMeta = (__bridge _YYModelPropertyMeta )(_propertyMeta); if (!propertyMeta->_setter) return; id value = nil; // 这里因为value 的值,对象的可能有keyPath,也有直接的key。所以用不同的方式来取value if (propertyMeta->_mappedToKeyArray) { value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray); } else if (propertyMeta->_mappedToKeyPath) { value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath); } else { value = [dictionary objectForKey:propertyMeta->_mappedToKey]; } if (value) { // 获取model 的指针 __unsafe_unretained id model = (__bridge id)(context->model); // 这里就是为model 赋值啦 ModelSetValueForProperty(model, value, propertyMeta); } } 复制代码
由于本人水平有限,文中如有不足之处,望大神指出。
如果你看完后觉得对你有所帮助,勿忘点赞
+关注
。
附本文的Demo,赠人玫瑰,手有余香。