YYModel是一款非常好用且非常轻量级的JSON模型转换库,源码一共就五个文件,去掉声明文件,所有的实现逻辑都在NSObject+YYModel.m
和YYClassInfo.m
这两个文件中,如图:
一、YYModel的代码结构
YYClassInfo功能主要是将Runtime层级中的一些api(结构体封)装到NSObject子类中,将runtime的api对象化,方便调用;NSObject+YYModel是提供调用的接口以及实现具体的模型转换逻辑,YYModel的代码结构如图:
1、YYClassInfo中对runtime的Api封装类
-
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
- Runtime中
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
- Runtime中
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;
2、NSObject+YYModel 数据结构的定义
- NSObject+YYModel 数据结构的定义
_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
二、YYModel的具体使用 --- NSObject+YYModel 提供调用的接口
1、JSON数据转换为model实体
- 1.json数据转model数据,
+ (nullable instancetype)yy_modelWithJSON:(id)json;
,内部调用2; - 2.NSDictionary数据转model数据,
+ (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary;
,内部调用4;LGModel *model = [LGModel yy_modelWithDictionary:dict];
- 3.json数据为model对象赋值,
- (BOOL)yy_modelSetWithJSON:(id)json;
,内部调用4; - 4.NSDictionary数据为model对象赋值,
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic;
,json转model的最终实现方法;LGModel *model1 = [[LGModel alloc]init]; [model1 yy_modelSetWithDictionary:dict];
2、model数据转换为JSON数据
- 1.model转为json数据,
- (nullable id)yy_modelToJSONObject;
,model数据转换为JSON数据,最终实现 - 2.model转为NSData,
- (nullable NSData *)yy_modelToJSONData;
,内部调用1 - 3.model转为json字符串,
- (nullable NSString *)yy_modelToJSONString;
,内部调用1
3、其他方法
- 对象深拷贝
- (nullable id)yy_modelCopy;
- 对象数据持久化存储
- 存:
- (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder;
- 取:
- (id)yy_modelInitWithCoder:(NSCoder *)aDecoder;
- 存:
- 对象hash值
- (NSUInteger)yy_modelHash;
- 对象是否相等
- (BOOL)yy_modelIsEqual:(id)model;
- 对象描述
- (NSString *)yy_modelDescription;
4、集合相应方法
- json-array集体实例化
/** 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;
- json-object集体实例化
/** 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;
5、协议方法 --- 用来处理映射中的各种问题
5.1、Model 属性名和 JSON 中的 Key 不相同 + (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;
,
//返回一个 Dict,将 Model 属性名对映射到 JSON 的 Key。
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"messageId":@[@"id",@"ID",@"book_id"]};
}
- 你可以把一个或一组
json key (key path)
映射到一个或多个属性。如果一个属性没有映射关系,那默认会使用相同属性名作为映射。 - 在 json->model 的过程中:如果一个属性对应了多个json key,那么转换过程会按顺序查找,并使用第一个不为空的值。
- 在 model->json 的过程中:如果一个属性对应了多个 json key (key path),那么转换过程仅会处理第一个 json key (key path);如果多个属性对应了同一个 json key,则转换过过程会使用其中任意一个不为空的值。
5.2、自定义容器中的实体类型映射 + (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
// 返回容器类中的所需要存放的数据类型 (以 Class 或 Class Name 的形式)。
+ (NSDictionary *)modelContainerPropertyGenericClass{
return @{@"books" : LGSubModel.class,
@"infoDict" : [LGPerson class],
@"likedUserIds" : @"NSNumber"
};
}
- 在实际使用过过程中,
[LGPerson class]
,LGPerson.class
,@"LGPerson"
没有明显的区别。
5.3、根据dic来实例不同类的类型 + (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
@implementation LGPerson
+(Class)modelCustomClassForDictionary:(NSDictionary *)dictionary {
if ([dictionary[@"gender"] integerValue] == 1) {
return LGMan.class;
}
return self;
}
@end
5.4、黑名单(不处理的属性)+ (nullable NSArray<NSString *> *)modelPropertyBlacklist;
5.5、白名单(只处理的属性) + (nullable NSArray<NSString *> *)modelPropertyWhitelist;
// 如果实现了该方法,则处理过程中会忽略该列表内的所有属性
+(NSArray<NSString *> *)modelPropertyBlacklist {
return @[@"subject"];
}
// 如果实现了该方法,则处理过程中不会处理该列表外的属性
+ (NSArray<NSString *> *)modelPropertyWhitelist {
return @[@"name",@"age",@"num"];
}
5.6、解析前更改字典信息 - (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
,发生在字典转模型之前,最后对网络字典做一次处理;
- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic{
if ([dic[@"gender"] integerValue] == 1) {
return nil;//不接受男性
}
return dic;
}
5.7、数据校验与自定义转换,字典转模型补充,- (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;
}
5.8、数据校验与自定义转换,模型转字典补充,- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
,同样,转为json时一样有格式或类型不正确,可以在这里重新赋值处理;
// 当 Model 转为 JSON 完成后,该方法会被调用。
// 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。
// 你也可以在这里做一些自动转换不能完成的工作。
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic {
if (!_createTime) {
return NO;
}
dic[@"timeInterval"] = @([_createTime timeIntervalSince1970]) ;
return YES;
}
三、YYModel实现json转model的源码逻辑
YYModel调用逻辑流程图:
1、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;
}
2、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;
}
2.1、缓存未取到类信息meta,根据model类cls去实例化该类信息, 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必须是字符串类型
///获取容器中存放对象的类(结果可能是元类或者NSString类对象)
Class meta = object_getClass(obj);
if (!meta) return;//防止异常,也就是说防止我们协议方法中返回的映射中Value是没有类对象或者元类的其他内容,如nil
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;
}
- 2.1.1、从Class中获取类信息,并封装成
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; }
- 2.1.1.1 更新类信息
_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; }
- 2.1.1.2 根据cls,初始化类信息,YYClassInfo的
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; }
- 2.1.1.1 更新类信息
- 2.1.2、实例化
_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;
}
3、真正来实现JSON模型转换功能的是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;
}
3.1、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中对两个回调函数的实现如下:
3.2、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);
}
3.3、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,赠人玫瑰,手有余香。