前言
阅读之前请见阅读YYModel源码详细解析-1,废话不多说,继续解析源码。
_YYModelMeta类
_YYModelMeta 来描述一个 YYClassInfo,完成所有的映射关系描述.
/// A class info in object model.
@interface _YYModelMeta : NSObject {
@package
YYClassInfo *_classInfo;
/// Key:mapped key and key path, Value:_YYModelPropertyInfo.
NSDictionary *_mapper;
/// Array<_YYModelPropertyMeta>, all property meta of this model.
NSArray *_allPropertyMetas;
/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
NSArray *_keyPathPropertyMetas;
/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
NSArray *_multiKeysPropertyMetas;
/// The number of mapped key (and key path), same to _mapper.count.
NSUInteger _keyMappedCount;
/// Model class type.
YYEncodingNSType _nsType;
/**
@protocol YYModel
@optional 协议是否实现
*/
BOOL _hasCustomWillTransformFromDictionary;
BOOL _hasCustomTransformFromDictionary;
BOOL _hasCustomTransformToDictionary;
BOOL _hasCustomClassFromDictionary;
}
- _mapper:存储 Json key 与 _PropertyMeta对象 的映射关系字典
- _allPropertyMetas:数组存放所有的 PropertyMeta
- _keyPathPropertyMetas:数组存放所有映射json keyPath 的 _PropertyMeta对象
- _multiKeysPropertyMetas:数组存放所有映射多个json key 的 _PropertyMeta对象(一对多)
- _keyMappedCount :属性总个数
- _nsType:解析Class是Foundation类型哪一种
- _hasCustomWillTransformFromDictionary、_hasCustomTransformFromDictionary、_hasCustomTransformToDictionary、_hasCustomClassFromDictionary,作用:判断YYModel协议方法是否实现
initWithClass方法
- (instancetype)initWithClass:(Class)cls {
// 初始化Class信息
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
if (!classInfo) return nil;
self = [super init];
// Get black list 黑名单
NSSet *blacklist = nil;
if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) { // 由用户传入的黑名单属性,不对其进行Json解析
NSArray *properties = [(id)cls modelPropertyBlacklist];
if (properties) {
blacklist = [NSSet setWithArray:properties];
}
}
// Get white list 白名单
NSSet *whitelist = nil;
if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) { // 由用户传入的白名单属性,解析
NSArray *properties = [(id)cls modelPropertyWhitelist];
if (properties) {
whitelist = [NSSet setWithArray:properties];
}
}
// 存储自定义映射Class字典
NSDictionary *genericMapper = nil;
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
genericMapper = [(id)cls modelContainerPropertyGenericClass];
if (genericMapper) { // 如果存在自定义映射Class,也就是说modelContainerPropertyGenericClass方法被实现了
NSMutableDictionary *tmp = [NSMutableDictionary new];
/**
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"photos" : YYPhoto.class,
@"likedUsers" : YYUser.class,
@"likedUserIds" : NSNumber.class};
}
*/
[genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { // 遍历字典
// 如果传入的Key是字典或者数组直接返回
if (![key isKindOfClass:[NSString class]]) return;
/// 根据key获得对应的Class类型
Class meta = object_getClass(obj);
// 合法性检查,meta为空
if (!meta) return;
//
if (class_isMetaClass(meta)) { // 如果传入的格式这样:YYUser.class 或 [YYUser class];或 Foundation Type
tmp[key] = obj;
} else if ([obj isKindOfClass:[NSString class]]) { // 如果传入的格式这样:@“YYUser”
Class cls = NSClassFromString(obj); // 获得一个类的名称;如果当前项目没有加载这个类,返回nil
if (cls) { // 有该类存在
tmp[key] = cls; // 以json Key为key,value为所指定的类存入字典
}
}
}];
// 遍历字典校验完毕后,传给自定义映射字典genericMapper
genericMapper = tmp;
}
}
// Create all property metas. 存储所有的property metas
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo; // 传入的Class
while (curClassInfo && curClassInfo.superCls != nil) { // recursive(递归) parse super class, but ignore root class (NSObject/NSProxy)(不对 Root Class((NSObject/NSProxy)) 做解析)
// 1. classInfo - > propertyInfo; 2. classInfo -> propertyInfo -> PropertyMeta
for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) { // 遍历class所有的属性
if (!propertyInfo.name) continue; // 合法性检查
if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;
// 初始化_YYModelPropertyMeta,classInfo -> propertyInfo -> PropertyMeta
_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]]; // 根据传入的参数,对PropertyMeta相关属性进行设置,返回元组信息
if (!meta || !meta->_name) continue;
if (!meta->_getter || !meta->_setter) continue; // 没有实现setter,getter
if (allPropertyMetas[meta->_name]) continue; // 已经解析过的直接跳过
allPropertyMetas[meta->_name] = meta; // 把属性的名称作为key存入allPropertyMetas,该字典存放所有的property metas
}
// 紧接着遍历父类信息
curClassInfo = curClassInfo.superClassInfo;
}
if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;
// create mapper
NSMutableDictionary *mapper = [NSMutableDictionary new];
NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) { // 是否创建映射
// 返回自定义属性映射字典
NSDictionary *customMapper = [(id )cls modelCustomPropertyMapper];
// 遍历自定义属性字典,进行校验
[customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) { // propertyName类中的属性名 mappedToKey json当中的属性名
// 根据属性名从allPropertyMetas取出对应的PropertyMetas
_YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
if (!propertyMeta) return; // // 如果allPropertyMetas不存在该属性,也就是说想要属性映射并不存在该属性,那么是无法映射的,直接返回。allPropertyMetas相当于每个类的集合,存放所有的属性
// 如果存在该属性,移除该属性,替换映射的属性名
[allPropertyMetas removeObjectForKey:propertyName];
// 确保映射的属性不是字典数组乱七八糟的类型
if ([mappedToKey isKindOfClass:[NSString class]]) { // 映射属性名 @"page" : @"p"
// 映射属性名合法性校验
/**
@"time":@"t";
*/
if (mappedToKey.length == 0) return;
// 属性名映射一个简单的Json Key
propertyMeta->_mappedToKey = mappedToKey; // 设置新属性名(映射的名称)
// 如果有多级映射 componentsSeparatedByString:将字符串切割成数组
/**
"ext" : {
"desc" : "A book written by J.K.Rowling."
},
格式1:
@"desc" : @"ext.desc",
存在点语法映射情况
keyPath---(
ext,
desc
)
格式2:
@{@"messageId":@"i",
@"content":@"c",
@"time":@"t"};
*/
NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."]; // @"desc" : @"ext.desc",
if (keyPath.count > 1) { // 属性名映射一个Json keypath,如果是多级映射,keyPath = @[@"ect",@"desc"];,保存属性映射的json keyPaths
propertyMeta->_mappedToKeyPath = keyPath;
// 属性添加到使用keypaths映射的数组
[keyPathPropertyMetas addObject:propertyMeta];
}
/** 字符串格式3 多个属性映射同一个mappedToKey
{@"name":@"name", @"title":@"name", @"tip":@"name"}
*/
// 将多级映射中Key设置下一个属性
// 使用Next指针串联mapper保存的mappedToKey当前映射的PropertyMeta对象
propertyMeta->_next = mapper[mappedToKey] ?: nil; // 第一次不执行
// 然后 再保存mappedToKey为key 映射 当前新的PropertyMeta对象
mapper[mappedToKey] = propertyMeta; // 1.以映射属性名为Key将(propertyMeta)属性列表存入字典,如果是格式1多级映射propertyMeta->_mappedToKeyPath存在
} else if ([mappedToKey isKindOfClass:[NSArray class]]) { // 如果映射的格式为 @"bookID": @[@"id", @"ID", @"book_id"]}; 同一个属性名映射到多个json key
NSMutableArray *mappedToKeyArray = [NSMutableArray new];
// 遍历 mappedToKey 数组
for (NSString *oneKey in ((NSArray *)mappedToKey)) {
// 每一个 数组元素oneKey 必须是字符串
if (![oneKey isKindOfClass:[NSString class]]) continue;
// 字符串合法性校验
if (oneKey.length == 0) continue;
// 多个属性如果还有多级映射关系,例如 @"bookID": @[@"id", @"ext.ID", @"book_id"]}
NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
if (keyPath.count > 1) { // NSArray
// keypath 当做一个数组保存到数组,例如保存@[@"ext",@"ID"]
[mappedToKeyArray addObject:keyPath];
} else { // NSString
// keypath 当做一个字符串保存到数组,例如保存id、book_id
[mappedToKeyArray addObject:oneKey];
}
// 因为是 `一个属性` 映射 `多个json key
// 只能保存第一个_mappedToKey(json key),否则后面的_mappedToKey被覆盖了
if (!propertyMeta->_mappedToKey) { // _mappedToKeyPath可能为nil;_mappedToKey,_mappedToKeyPath这两个属性可以为后面的方法区别用多级映射(multi)或者一级映射方法
propertyMeta->_mappedToKey = oneKey; // 保存数组中第一个oneKey,例如id
propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil; // 保存数组中第一个_mappedToKeyPath,例如ext.ID
}
}
// 属性 没有映射任何的json key
if (!propertyMeta->_mappedToKey) return;
// 保存 当前属性描述的所有json key 映射数组
propertyMeta->_mappedToKeyArray = mappedToKeyArray;
// 记录一个 <1属性:n 个 jsonkey>映射关系 的属性
[multiKeysPropertyMetas addObject:propertyMeta];
// 多(属性)对一(json key)
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
}
}];
}
// 注意,如上只对映射字典中给出的属性进行处理
// 如上处理过的属性都从属性字典中删除
// allPropertyMetas此时剩下的属性,都是没有配置映射规则的
// 处理没有映射配置的属性
// 默认 属性映射json key >>>> 属性的名字
[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
// 简单属性映射
propertyMeta->_mappedToKey = name;
// 让映射到相同json key的 不同属性(多属性对一个json key) 使用next指针串联
propertyMeta->_next = mapper[name] ?: nil;
// 保存新的映射json key
mapper[name] = propertyMeta;
}];
// 修正映射配置数据
if (mapper.count) _mapper = mapper;
if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
_classInfo = classInfo;
_keyMappedCount = _allPropertyMetas.count;
_nsType = YYClassGetNSType(cls);
/**
@protocol YYModel
@optional
*/
/**
instancesRespondToSelector:被调用时,动态方法是有机会的首先为selector提供一个IMP,如果该类对应的Property有modelCustomWillTransformFromDictionary/modelCustomTransformFromDictionary/modelCustomTransformToDictionary/_hasCustomClassFromDictionary方法实现方法,则返回YES
**/
_hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
_hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
_hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
_hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
return self;
}
metaWithClass方法
/// Returns the cached model class meta 缓存优化 Class 与 _YYModelMeta对象
+ (instancetype)metaWithClass:(Class)cls {
// 空处理
if (!cls) return nil;
// 单例字典来缓存优化处理_YYModelMeta对象,类似YYClassInfo对象
static CFMutableDictionaryRef cache; // 字典:保存数据
static dispatch_once_t onceToken;
// dispatch_semaphore_t 计数器
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
// // 初始化字典
cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
/** dispatch_semaphore_create
创建新的计数信号量的初始值。
新创建的信号量,或nil失败。
创建一个信号量,只允许一个线程通过
*/
lock = dispatch_semaphore_create(1);
});
/** dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 如果semaphore计数大于等于1.计数-1,返回,程序继续运行。如果计数为0,则等待。DISPATCH_TIME_FOREVER这里设置的等待时间是一直等待。dispatch_semaphore_signal(semaphore);计数+1.在这两句代码中间的执行代码,每次只会允许一个线程进入,这样就有效的保证了在多线程环境下,只能有一个线程进入。
**/
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
// // 查询缓存获取_YYModelMeta对象
_YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
// dispatch_semaphore_signal(semaphore);计数+1.在这两句代码中间的执行代码,每次只会允许一个线程进入,这样就有效的保证了在多线程环境下,只能有一个线程进入。
dispatch_semaphore_signal(lock);
if (!meta || meta->_classInfo.needUpdate) {
// 初始化_YYModelMeta传入cls对象设置相应的属性 = 更新
meta = [[_YYModelMeta alloc] initWithClass:cls];
if (meta) { // 更新成功
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
// 将更新的结果覆盖原缓存记录
CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
dispatch_semaphore_signal(lock);
}
}
return meta;
}
/**
Get number from property.()
@discussion Caller should hold strong reference to the parameters before this function returns.
@param model Should not be nil.
@param meta Should not be nil, meta.isCNumber should be YES, meta.getter should not be nil.
@return A number object, or nil if failed.
从_YYModelPropertyMeta将c基本数值类型的属性 统一按照 NSNumber 处理。
discussion:调用方在函数返回之前对参数具有很强的引用。
@param 模型不应该是nil。
@param meta不应为零,meta.iscnumber应该为YES,meta.getter不应该是nil。
“返回一个number对象,如果failed返回nil,
格式:
((void (*)(id, SEL, 方法形参类型))(void *) objc_msgSend)(对象, SEL, 方法执行的参数);
*/
static force_inline NSNumber *ModelCreateNumberFromProperty(__unsafe_unretained id model,
__unsafe_unretained _YYModelPropertyMeta *meta) {
// 按照不同数据类型取值,并转换成NSNumber
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool: {
return @(((bool (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt8: {
return @(((int8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt8: {
return @(((uint8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt16: {
return @(((int16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt16: {
return @(((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt32: {
return @(((int32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt32: {
return @(((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt64: {
return @(((int64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt64: {
return @(((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeFloat: {
float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
if (isnan(num) || isinf(num)) return nil;
return @(num);
}
case YYEncodingTypeDouble: {
double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
if (isnan(num) || isinf(num)) return nil;
return @(num);
}
case YYEncodingTypeLongDouble: {
double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
if (isnan(num) || isinf(num)) return nil;
return @(num);
}
default: return nil;
}
}
/**
Set number to property.
@discussion Caller should hold strong reference to the parameters before this function returns.
@param model Should not be nil.
@param num Can be nil.
@param meta Should not be nil, meta.isCNumber should be YES, meta.setter should not be nil.
获取 c基本数值类型的属性值
discussion: 调用方在函数返回之前对参数具有很强的引用。
@param:model 被_YYModelMeta解析过的实体类对象
@param: num 要设置的NSNumber值
@param:meta:(要设置值的属性的描述对象) meta不应为nil,meta.iscnumber应该为YES,meta.setter不应该为nil
格式:
((void (*)(id, SEL, 方法形参类型))(void *) objc_msgSend)(对象, SEL, 方法执行的参数);
*/
static force_inline void ModelSetNumberToProperty(__unsafe_unretained id model,
__unsafe_unretained NSNumber *num,
__unsafe_unretained _YYModelPropertyMeta *meta) {
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool: {
/** objc_msgSend
用一个简单的返回值发送消息到一个类的实例。
方法的返回值。
self
指向类的实例的一个指针,该实例将接收该消息。
OP
处理消息的方法的选择器。
…
一个包含参数的变量参数列表。
返回方法的返回值。
*/
((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)model, meta->_setter, num.boolValue);
} break;
case YYEncodingTypeInt8: {
((void (*)(id, SEL, int8_t))(void *) objc_msgSend)((id)model, meta->_setter, (int8_t)num.charValue);
} break;
case YYEncodingTypeUInt8: {
((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint8_t)num.unsignedCharValue);
} break;
case YYEncodingTypeInt16: {
((void (*)(id, SEL, int16_t))(void *) objc_msgSend)((id)model, meta->_setter, (int16_t)num.shortValue);
} break;
case YYEncodingTypeUInt16: {
((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint16_t)num.unsignedShortValue);
} break;
case YYEncodingTypeInt32: {
((void (*)(id, SEL, int32_t))(void *) objc_msgSend)((id)model, meta->_setter, (int32_t)num.intValue);
}
case YYEncodingTypeUInt32: {
((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint32_t)num.unsignedIntValue);
} break;
case YYEncodingTypeInt64: {
if ([num isKindOfClass:[NSDecimalNumber class]]) {
((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);
} else {
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.longLongValue);
}
} break;
case YYEncodingTypeUInt64: {
if ([num isKindOfClass:[NSDecimalNumber class]]) {
((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);
} else {
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.unsignedLongLongValue);
}
} break;
case YYEncodingTypeFloat: {
float f = num.floatValue;
if (isnan(f) || isinf(f)) f = 0;
((void (*)(id, SEL, float))(void *) objc_msgSend)((id)model, meta->_setter, f);
} break;
case YYEncodingTypeDouble: {
double d = num.doubleValue;
if (isnan(d) || isinf(d)) d = 0;
((void (*)(id, SEL, double))(void *) objc_msgSend)((id)model, meta->_setter, d);
} break;
case YYEncodingTypeLongDouble: {
long double d = num.doubleValue;
if (isnan(d) || isinf(d)) d = 0;
((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)model, meta->_setter, (long double)d);
} // break; commented for code coverage in next line
default: break;
}
}
/**
Set value to model with a property meta.
@discussion Caller should hold strong reference to the parameters before this function returns.
@param model Should not be nil.
@param value Should not be nil, but can be NSNull.
@param meta Should not be nil, and meta->_setter should not be nil.
根据对象属性的描述, 将id类型的对象设置给属性
*/
static void ModelSetValueForProperty(__unsafe_unretained id model,
__unsafe_unretained id value,
__unsafe_unretained _YYModelPropertyMeta *meta) {
/** 根据对象属性的描述,来设置id类型的属性值,防止类型转换错误导致崩溃
- 属性`变量类型`
- 设置给属性的`值类型`
*/
if (meta->_isCNumber) { // 如果是C语言基本数字类型(注意: NSNumber不是基本类型)
// id value >>> NSNumber
NSNumber *num = YYNSNumberCreateFromID(value);
// 将NSNumber值 设置给实体对象的属性
ModelSetNumberToProperty(model, num, meta);
if (num) [num class]; // hold the number 释放num
} else if (meta->_nsType) { // 如果属性变量是Foundation框架
if (value == (id)kCFNull) { // 值为空,直接向model的setter发送nil消息
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
} else {
switch (meta->_nsType) { // 区分不同的Foundation Class类型设值
case YYEncodingTypeNSString: // 如果属性变量是NSString
case YYEncodingTypeNSMutableString: { // 如果是类型是NSMutableString
// 依次判断value类型
if ([value isKindOfClass:[NSString class]]) {// 值的类型NSString
if (meta->_nsType == YYEncodingTypeNSString) { // 属性类型是NSString
// 向model的setter发送value消息
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value); // copy
} else { // 属性类型是NSMutaleString
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy); // mutableCopy
}
} else if ([value isKindOfClass:[NSNumber class]]) { // value类型为NSNumber
// 判断类型是NSString/NSMutaleString,向model的setter发送value消息
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
(meta->_nsType == YYEncodingTypeNSString) ?
((NSNumber *)value).stringValue :
((NSNumber *)value).stringValue.mutableCopy);
} else if ([value isKindOfClass:[NSData class]]) { // value类型为NSData
NSMutableString *string = [[NSMutableString alloc] initWithData:value encoding:NSUTF8StringEncoding];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, string);
} else if ([value isKindOfClass:[NSURL class]]) { // value类型为NSURL
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
(meta->_nsType == YYEncodingTypeNSString) ?
((NSURL *)value).absoluteString :
((NSURL *)value).absoluteString.mutableCopy);
} else if ([value isKindOfClass:[NSAttributedString class]]) {// value类型为NSAttributedString
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
(meta->_nsType == YYEncodingTypeNSString) ?
((NSAttributedString *)value).string :
((NSAttributedString *)value).string.mutableCopy);
}
} break;
case YYEncodingTypeNSValue:
case YYEncodingTypeNSNumber:
case YYEncodingTypeNSDecimalNumber: {
if (meta->_nsType == YYEncodingTypeNSNumber) { // 属性变量类型是Foundation框架NSNumber
// value id -> NSNumber
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSNumberCreateFromID(value));
} else if (meta->_nsType == YYEncodingTypeNSDecimalNumber) { // 属性变量类型是Foundation框架NSDecimalNumber
if ([value isKindOfClass:[NSDecimalNumber class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else if ([value isKindOfClass:[NSNumber class]]) {
NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);
} else if ([value isKindOfClass:[NSString class]]) {
NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithString:value];
NSDecimal dec = decNum.decimalValue;
if (dec._length == 0 && dec._isNegative) {
decNum = nil; // NaN
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);
}
} else { // YYEncodingTypeNSValue
if ([value isKindOfClass:[NSValue class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
}
}
} break;
case YYEncodingTypeNSData:
case YYEncodingTypeNSMutableData: {
if ([value isKindOfClass:[NSData class]]) {
if (meta->_nsType == YYEncodingTypeNSData) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
NSMutableData *data = ((NSData *)value).mutableCopy;
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);
}
} else if ([value isKindOfClass:[NSString class]]) {
NSData *data = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding];
if (meta->_nsType == YYEncodingTypeNSMutableData) {
data = ((NSData *)data).mutableCopy;
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);
}
} break;
// 属性变量 NSDate
case YYEncodingTypeNSDate: {
if ([value isKindOfClass:[NSDate class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else if ([value isKindOfClass:[NSString class]]) {//value类型是NSString
// YYNSDateFromString: NSString转换NSDate
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSDateFromString(value));
}
} break;
case YYEncodingTypeNSURL: {
if ([value isKindOfClass:[NSURL class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else if ([value isKindOfClass:[NSString class]]) { // value属性类型是NSString,NSString - > NSURL
// 字符串去掉多余的空格
NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *str = [value stringByTrimmingCharactersInSet:set];
if (str.length == 0) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, nil);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, [[NSURL alloc] initWithString:str]);
}
}
} break;
// 属性变量 NSArray、NSMutableArray
case YYEncodingTypeNSArray:
case YYEncodingTypeNSMutableArray: {
// 看是否配置有自定义映射数组每一个对象的Class映射
if (meta->_genericCls) {
// 有数组元素Class配置
NSArray *valueArr = nil;
// 值类型只能是: NSArray、NSSet
if ([value isKindOfClass:[NSArray class]]) valueArr = value; // 值是数组
else if ([value isKindOfClass:[NSSet class]]) valueArr = ((NSSet *)value).allObjects; // 值是集合
// 转换传入的值数组 成为 实体类对象的数组
if (valueArr) {
// 保存所有转成实体对象的数组
NSMutableArray *objectArr = [NSMutableArray new];
// 遍历数组每一个对象
for (id one in valueArr) {
// 支持数组元素类型: 实体类对象、字典对象
if ([one isKindOfClass:meta->_genericCls]) {
// 数组元素Class == 配置有映射Class
[objectArr addObject:one];
} else if ([one isKindOfClass:[NSDictionary class]]) {
// 数组元素Class == NSDictionary Class,需要转换成实体
// 获取 属性 配置的 实体类Class
Class cls = meta->_genericCls;
if (meta->_hasCustomClassFromDictionary) { // 有自定义Class映射字典
// 先使用用户设置的修正字典的Class
// 使用 -[NSObject modelCustomClassForDictionary:] 传入当前字典对象,得到的对应Class
cls = [cls modelCustomClassForDictionary:one];
// 如果没有设置,就使用当前属性配置的Class
//使用 -[NSObject mapping_containerPropertiesMappings]返回的属性对应的Class
if (!cls) cls = meta->_genericCls;
}
// 创建一个新的NSObject对象
NSObject *newOne = [cls new];
[newOne modelSetWithDictionary:one];
if (newOne) [objectArr addObject:newOne];
}
}
// 将转换好的数组设置给属性
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);
}
} else { // 没有数组元素Class配置
// value是NSArray
if ([value isKindOfClass:[NSArray class]]) {
if (meta->_nsType == YYEncodingTypeNSArray) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSArray *)value).mutableCopy);
}
} else if ([value isKindOfClass:[NSSet class]]) { // value是NSSet
if (meta->_nsType == YYEncodingTypeNSArray) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSSet *)value).allObjects);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSSet *)value).allObjects.mutableCopy);
}
}
}
} break;
case YYEncodingTypeNSDictionary:
case YYEncodingTypeNSMutableDictionary: {
if ([value isKindOfClass:[NSDictionary class]]) {
if (meta->_genericCls) {
NSMutableDictionary *dic = [NSMutableDictionary new];
[((NSDictionary *)value) enumerateKeysAndObjectsUsingBlock:^(NSString *oneKey, id oneValue, BOOL *stop) {
if ([oneValue isKindOfClass:[NSDictionary class]]) {
Class cls = meta->_genericCls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:oneValue];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
NSObject *newOne = [cls new];
[newOne modelSetWithDictionary:(id)oneValue];
if (newOne) dic[oneKey] = newOne;
}
}];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, dic);
} else {
if (meta->_nsType == YYEncodingTypeNSDictionary) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSDictionary *)value).mutableCopy);
}
}
}
} break;
// 属性变量 NSArray、NSMutableArray
case YYEncodingTypeNSSet:
case YYEncodingTypeNSMutableSet: {
NSSet *valueSet = nil;
// 属性变量是NSArray类型时
// 看是否配置有数组中每一个对象的Class映射
// value类型只能是: NSArray、NSSet
if ([value isKindOfClass:[NSArray class]]) valueSet = [NSMutableSet setWithArray:value];
else if ([value isKindOfClass:[NSSet class]]) valueSet = ((NSSet *)value);
// 有数组元素Class配置
if (meta->_genericCls) {
NSMutableSet *set = [NSMutableSet new];
for (id one in valueSet) { // 遍历数组每一个对象
if ([one isKindOfClass:meta->_genericCls]) {
[set addObject:one];
} else if ([one isKindOfClass:[NSDictionary class]]) {
Class cls = meta->_genericCls;
if (meta->_hasCustomClassFromDictionary) { // 是否实现映射字典
// 数组元素Class == NSDictionary Class,需要转换成实体
// 获取 属性 配置的 实体类Class
cls = [cls modelCustomClassForDictionary:one];
// 支持数组元素类型: 实体类对象、字典对象
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
// 创建一个新的NSObject对象
NSObject *newOne = [cls new];
[newOne modelSetWithDictionary:one];
if (newOne) [set addObject:newOne];
}
}
// 将转换好的数组设置给属性
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, set);
} else {
if (meta->_nsType == YYEncodingTypeNSSet) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, valueSet);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSSet *)valueSet).mutableCopy);
}
}
} // break; commented for code coverage in next line
default: break;
}
}
} else {
BOOL isNull = (value == (id)kCFNull);
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject: {
if (isNull) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
} else if ([value isKindOfClass:meta->_cls] || !meta->_cls) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value);
} else if ([value isKindOfClass:[NSDictionary class]]) {
NSObject *one = nil;
if (meta->_getter) {
one = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
}
if (one) {
[one modelSetWithDictionary:value];
} else {
Class cls = meta->_cls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:value];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
one = [cls new];
[one modelSetWithDictionary:value];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one);
}
}
} break;
case YYEncodingTypeClass: {
if (isNull) {
((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)NULL);
} else {
Class cls = nil;
if ([value isKindOfClass:[NSString class]]) {
cls = NSClassFromString(value);
if (cls) {
((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)cls);
}
} else {
cls = object_getClass(value);
if (cls) {
if (class_isMetaClass(cls)) {
((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)value);
}
}
}
}
} break;
case YYEncodingTypeSEL: {
if (isNull) {
((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)NULL);
} else if ([value isKindOfClass:[NSString class]]) {
SEL sel = NSSelectorFromString(value);
if (sel) ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)sel);
}
} break;
case YYEncodingTypeBlock: {
// Block类型使用: void (^)(void)
if (isNull) {
((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())NULL);
} else if ([value isKindOfClass:YYNSBlockClass()]) {
((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())value);
}
} break;
// 注意: c类型需要使用NSValue对象包装
case YYEncodingTypeStruct:
case YYEncodingTypeUnion:
case YYEncodingTypeCArray: {
if ([value isKindOfClass:[NSValue class]]) {
// 传入value的编码
const char *valueType = ((NSValue *)value).objCType;
// 属性变量类型的编码
const char *metaType = meta->_info.typeEncoding.UTF8String;
// 比较两个编码的内容是否一致
if (valueType && metaType && strcmp(valueType, metaType) == 0) {
// 结构体实例使用KVC设置给属性变量
[model setValue:value forKey:meta->_name];
}
}
} break;
// 指针类型value,使用 void* 万能指针类型
case YYEncodingTypePointer:
case YYEncodingTypeCString: {
if (isNull) {
((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, (void *)NULL);
} else if ([value isKindOfClass:[NSValue class]]) {
NSValue *nsValue = value;
// 判断传入值的类型,是否是 void* 指针类型
// TODO: 为什么CString要按照void*指针类型了?
// 因为 [NSValue valueWithPointer:(nullable const void *)]; 将传入的指针转换成 void* 类型了
// 所以再通过NSValue获取到指针的类型时,就是 void* ,而其编码就是 `^v`
if (nsValue.objCType && strcmp(nsValue.objCType, "^v") == 0) {
((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, nsValue.pointerValue);
}
}
} // break; commented for code coverage in next line
default: break;
}
}
}
typedef struct {
// 实体类Class的描述类
void *modelMeta; ///< _YYModelMeta
// 设置给哪个实体类对象
void *model; ///< id (self)
// 要设置的值
void *dictionary; ///< NSDictionary (json)
} ModelSetContext;
/**
Apply function for dictionary, to set the key-value pair to model.
@param _key should not be nil, NSString.
@param _value should not be nil.
@param _context _context.modelMeta and _context.model should not be nil.
应用字典函数,设置键值对模型。
@param _key不应为nil,NSString。
@param _value不应为nil。
@param _context _context.modelmeta和_context.model不应该是NIL。
*/
/**
* 直接给出jsonkey,然后找到映射的属性描述
*
* @param _jsonKey json key
* @param _value 要设置的value
* @param _context ModelSetContext实例
*/
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
// c类型转换oc类型
ModelSetContext *context = _context;
__unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
// 从_YYModelMeta->_mapper字典,获取_mapper对应的_YYModelPropertyMeta对象
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
// c类型转换oc类型,实体类对象
__unsafe_unretained id model = (__bridge id)(context->model);
// 当有多个不同属性映射一个_mapper
while (propertyMeta) {
// 设置当前属性
if (propertyMeta->_setter) {
// 重要方法:设置键值对模型
// 根据属性描述,将value设置给实体类对象
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
}
// 当前_mapper有多个属性同时映射
propertyMeta = propertyMeta->_next;
};
}
/**
Apply function for model property meta, to set dictionary to model.
@param _propertyMeta should not be nil, _YYModelPropertyMeta.
@param _context _context.model and _context.dictionary should not be nil.
应用模型属性元函数,对模型集进行字典。
@param _propertymeta不应为nil,_yymodelpropertymeta。
@param _context _context.model和_context.dictionary不应该是nil。
*/
/**
* 先传入属性描述,再从属性描述获取属性映射的jsonkey,然后从传入的字典获取jsonvalue,然后设置给实体类对象
*
* @param _propertyMeta 属性描述对象
* @param context ModelSetContext实例
*/
static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {
// 类型转换
ModelSetContext *context = _context;
// 从context获取字典对象
__unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
// 属性必须存在setter方法实现
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);
if (!propertyMeta->_setter) return;
id value = nil;
if (propertyMeta->_mappedToKeyArray) {
// 属性映射多个_mappedToKey(1.一个字符串key 2.keypath)
value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
} else if (propertyMeta->_mappedToKeyPath) {
// 属性映射一个_mappedToKeyPath
value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
} else {
// 属性映射一个单独的json Ke
value = [dictionary objectForKey:propertyMeta->_mappedToKey];
}
// 将从json字典取出的值设置给实体对象
if (value) {
// 取出Context中的实体类对象
__unsafe_unretained id model = (__bridge id)(context->model);
// 调用给对象设置id值的c方法
ModelSetValueForProperty(model, value, propertyMeta);
}
}