1. YYModel 转化流程是怎么样的?
1. 由模型类封装YYModelMeta
首先,YYModel将模型类进行一次YYClassInfo信息封装,其中包括对变量封装(YYClassIvarInfo),对属性封装(YYClassProperityInfo),对成员方法封装(YYClassMethodInfo)。
其次,YYModel通过给用户提供的映射协议,让用户根据模型实现映射协议,同时基于封装的YYClassPeoperityInfo,最终封装成YYModelPropertyMeta。而在YYModelPropertyMeta中已经初始化了对应的映射内容。
最后,由YYClassInfo、已经形成映射关系的属性YYModelPropertyMeta数组等封装成了YYModelMeta。
2. 根据YYModelMeta,将dic内容转化为模型对象内容
根据YYModelMeta中的属性进行setter方法的objc_MsgSend调用,从而最终将值赋予对应的属性。
2.YYModel如何封装YYClassInfo的呢?
YYClassInfo组合了YYClassIvarInfo、YYClassMethodInfo、YYClassProperityInfo。其分别表示类的成员变量信息、类的方法信息、类的属性信息。以下将分别对方法、属性、变量进行分析。
类的方法列表—YYClassMethodInfo分析:
//通过类名,获取到类的实例方法
Method *methods = class_copyMethodList(cls, &methodCount);
if (methods) {
NSMutableDictionary *methodInfos = [NSMutableDictionary new];
_methodInfos = methodInfos;
//分别将方法信息解析到YYClassMethodInfo中
for (unsigned int i = 0; i < methodCount; i++) {
YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];
//将方法名与YYClassMethodInfo以键值对的形式保存起来
if (info.name) methodInfos[info.name] = info;
}
//注意,对于c层次的变量,我们需要使用free释放
free(methods);
}
其中ClassMethodInfo就包括类方法的Method、Sel、Imp、方法参数类型数组、方法返回类型等等。
- (instancetype)initWithMethod:(Method)method {
if (!method) return nil;
self = [super init];
_method = method;
_sel = method_getName(method);
_imp = method_getImplementation(method);
const char *name = sel_getName(_sel);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
//获取方法的编码字符串,它是一串由返回值和参数拼接而成。
const char *typeEncoding = method_getTypeEncoding(method);
if (typeEncoding) {
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
}
----注解:(我们举一个例子来分析方法的类型编码对应的意思:
NSLog(@"%s",method_getTypeEncoding(class_getInstanceMethod([TestModel class], @selector(hide:))));
输出:v24@0:8@16,其中v表示返回类型为void,24表示方法的所有参数占据的总字节数,@0表示方法对象中在offset为0的地方有一个对象参数(self对象),:8表示在方法对象中在offset为8字节的地方有个sel参数(方法自己),@16表示在方法对象中在offset为16个字节的位置有对象类型的参数。)
//获取方法返回类型
char *returnType = method_copyReturnType(method);
if (returnType) {
_returnTypeEncoding = [NSString stringWithUTF8String:returnType];
free(returnType); //释放对应的对象
}
unsigned int argumentCount = method_getNumberOfArguments(method);
if (argumentCount > 0) {
NSMutableArray *argumentTypes = [NSMutableArray new];
for (unsigned int i = 0; i < argumentCount; i++) {
//获取方法参数类型(默认情况下,方法存在self和command两个隐藏的参数,分表表示方法类和方法自己
char *argumentType = method_copyArgumentType(method, i);
NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
[argumentTypes addObject:type ? type : @""];
if (argumentType) free(argumentType);
}
//最终将方法参数类型保存起来。
_argumentTypeEncodings = argumentTypes;
}
其中获取的参数类型可以比对以下编码格式表格:
获取类的属性列表—–YYClassProperityInfo
//获取类的属性列表
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++) {
YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
if (info.name) propertyInfos[info.name] = info;
}
free(properties);
}
其中YYClassPropertyInfo的创建过程如下:
- (instancetype)initWithProperty:(objc_property_t)property {
if (!property) return nil;
self = [super init];
_property = property;
//获取属性名
const char *name = property_getName(property);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
YYEncodingType type = 0;
unsigned int attrCount;
//获取属性特性对象
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
for (unsigned int i = 0; i < attrCount; i++) {
switch (attrs[i].name[0]) {
//表示属性的类型,它是以kv的形式存在
case 'T': { // Type encoding
if (attrs[i].value) {
_typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
//将类型转化为对应的类型
type = YYEncodingGetType(attrs[i].value);
if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) {
NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
if (![scanner scanString:@"@\"" intoString:NULL]) continue;
NSString *clsName = nil;
if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
if (clsName.length) _cls = objc_getClass(clsName.UTF8String);
}
NSMutableArray *protocols = nil;
while ([scanner scanString:@"<" intoString:NULL]) {
NSString* protocol = nil;
if ([scanner scanUpToString:@">" intoString: &protocol]) {
if (protocol.length) {
if (!protocols) protocols = [NSMutableArray new];
[protocols addObject:protocol];
}
}
[scanner scanString:@">" intoString:NULL];
}
_protocols = protocols;
}
}
} break;
case 'V': { // 实例变量
if (attrs[i].value) {
_ivarName = [NSString stringWithUTF8String:attrs[i].value];
}
} break;
case 'R': { //只读
type |= YYEncodingTypePropertyReadonly;
} break;
case 'C': { //copy
type |= YYEncodingTypePropertyCopy;
} break;
case '&': { //retain
type |= YYEncodingTypePropertyRetain;
} break;
case 'N': { //非原子操作
type |= YYEncodingTypePropertyNonatomic;
} break;
case 'D': {
type |= YYEncodingTypePropertyDynamic;
} break;
case 'W': { //weak
type |= YYEncodingTypePropertyWeak;
} break;
case 'G': { //getter
type |= YYEncodingTypePropertyCustomGetter;
if (attrs[i].value) {
_getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} break;
case 'S': { //setter
type |= YYEncodingTypePropertyCustomSetter;
if (attrs[i].value) {
_setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} // break; commented for code coverage in next line
default: break;
}
}
//释放属性特性对象
if (attrs) {
free(attrs);
attrs = NULL;
}
_type = type;
if (_name.length) {
if (!_getter) {
_getter = NSSelectorFromString(_name);
}
if (!_setter) {
_setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
}
}
return self;
}
属性结构如下:
struct property_t {
const char *name;
const char *attributes;
};
其中name表示属性的名字,而attributes表示一串用‘,’分割的属性特性字符串,而attributes中‘,’分隔的字符串是以kv的形式展示的。其中objc_property_attribute_t结构如下:
typedef struct {
const char * _Nonnull name; /**< The name of the attribute */
const char * _Nonnull value; /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;
objc_property_attribute_t中常用的name和value如下表示:
常用attribute name value
nonatomic "N" ""
strong/retain "&" ""
weak "W" ""
属性的类型type "T" "@TypeName", 比如"@\"NSString\""
属性对应的实例变量Ivar "V" "Ivar_name", eg "_name"
readonly "R" ""
getter "G" "GetterName", eg"isRight"
setter "S" "SetterName", eg"setName"
assign/atomic 默认即为assign和retain
我们举个例子例子分析:
@property (nonatomic,strong,readwrite) NSString * uid
以上这个属性在运行时获取到的属性特性为:T@"NSString",&,N,V_uid,分别表示此属性是NSString类型,它是strong的,同时是非原子操作(nonatomic),同时属性对应的成员变量为_uid
私有成员变量—YYClassIvarInfo
//获取私有成员变量
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 *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]];
if (info.name) ivarInfos[info.name] = info;
}
free(ivars);
}
YYModel中通过YYClassIvarInfo封装了ivar变量,具体获取变量操作如下:
- (instancetype)initWithIvar:(Ivar)ivar {
if (!ivar) return nil;
self = [super init];
_ivar = ivar;
//获取变量名
const char *name = ivar_getName(ivar);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
//记录变量相对于当前类对象中的位置偏移量,单位byte
_offset = ivar_getOffset(ivar);
//根据编码字符串获取到对应的变量类型
const char *typeEncoding = ivar_getTypeEncoding(ivar);
if (typeEncoding) {
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
//针对类型编码过的类型,通过以下方法进行类型解码
_type = YYEncodingGetType(typeEncoding);
}
我们比对IVar结构体:
struct ivar_t {
int32_t *offset;
const char *name;
const char *type;
// alignment is sometimes -1; use alignment() instead
uint32_t alignment_raw;
uint32_t size;
uint32_t alignment() const {
if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
return 1 << alignment_raw;
}
};
封装的YYClassIvarInfo是和ivar_t中结构一一对应的(除了内存对齐)
YYModelMeta是如何创建的呢
@interface _YYModelMeta : NSObject {
@package
YYClassInfo *_classInfo; //类封装信息
/// Key:mapped key and key path, Value:_YYModelPropertyMeta.
NSDictionary *_mapper; //key为属性(没有映射字段)/映射字段,value是YYModelPropertyMeta
/// Array<_YYModelPropertyMeta>, all property meta of this model.
NSArray *_allPropertyMetas; //保存所有YYModelPropertyMeta数组
/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
NSArray *_keyPathPropertyMetas; //保存映射key是 keyPath形式的YYModelPropertyMeta
/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
NSArray *_multiKeysPropertyMetas;//保存映射key是多个的对应的modelPropertyMeta
/// The number of mapped key (and key path), same to _mapper.count.
NSUInteger _keyMappedCount;
/// Model class type.
YYEncodingNSType _nsType;
BOOL _hasCustomWillTransformFromDictionary;
BOOL _hasCustomTransformFromDictionary;
BOOL _hasCustomTransformToDictionary;
BOOL _hasCustomClassFromDictionary;
}
//进行元数据的初始化过程
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil;
//使用Foundation层的字典效率相对Cocoa层面的高
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);
_YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
dispatch_semaphore_signal(lock);
//如果元数据没有创建过或者创建过但是没有更新缓存的情况下,进行数据更新
if (!meta || meta->_classInfo.needUpdate) {
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;
}
以下我们将分析一下_YYModelMeta的初始化过程:
- (instancetype)initWithClass:(Class)cls {
//获取类信息
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
if (!classInfo) return nil;
self = [super init];
//创建黑名单,对于一些不需要转化或者操作的属性,可以在这个协议回调中添加进去,从而让转化更加有效率。
NSSet *blacklist = nil;
if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
if (properties) {
blacklist = [NSSet setWithArray:properties];
}
}
// 创建白名单,在字典转化模型过程中对于一些必须要转化的属性,我们将它添加到白名单中
NSSet *whitelist = nil;
if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
if (properties) {
whitelist = [NSSet setWithArray:properties];
}
}
//针对容器对象,我们找到容器对象中的数据类型,并保存起来
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) {
if (![key isKindOfClass:[NSString class]]) return;
Class meta = object_getClass(obj);
if (!meta) return;
//由于[Classxx class]返回的是类对象的类,即元类
if (class_isMetaClass(meta)) {
tmp[key] = obj;
} else if ([obj isKindOfClass:[NSString class]]) {
Class cls = NSClassFromString(obj);
if (cls) {
tmp[key] = cls;
}
}
}];
genericMapper = tmp;
}
}
//创建模型属性对象
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
//对YYClassPropertyInfo进行遍历
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;
//创建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;
其中我们可以仔细看下YYModelPropertyMeta的创建过程:
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
//对于容器类属性,存在generic类,但是如果不是容器类,而是协议属性,我们可以找到对应协议数组中的某个协议来作为属性的类型
if (!generic && propertyInfo.protocols) {
for (NSString *protocol in propertyInfo.protocols) {
Class cls = objc_getClass(protocol.UTF8String);
if (cls) {
generic = cls;
break;
}
}
}
//创建YYModelPropertyMeta对象并初始化
_YYModelPropertyMeta *meta = [self new];
meta->_name = propertyInfo.name; //属性名
meta->_type = propertyInfo.type;//属性类型
meta->_info = propertyInfo; //属性的YYClassPropertyInfo对象
meta->_genericCls = generic; //如果是容器类对象和协议类对象
//如果属性是对象类型,我们设置meta的基于foundation框架的对象类型。
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:
*/
//对于NSKeyedUnarchiver支持的结构体类型如下:
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不支持long double和指针类型
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: {
//设置kvc兼容
meta->_isKVCCompatible = YES;
} break;
default: break;
}
}
return meta;
}
以上我们了解到propertyMeta设置了属性名,属性类型,如果是容器类属性,则设置了容器中的属性类型,设置foundation的类型,是否是数字类型,是否是支持的archive类型,setter,getter即kvc兼容等
以下我们分析下maper处理:
//一对一的映射
NSMutableDictionary *mapper = [NSMutableDictionary new];
//含有属性.属性路径的映射,则存储在keyPathPropertyMetas中
NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
//多个keys对应的映射数组
NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
//获取映射字典
NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
//遍历映射字典
[customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
//设置propertyMeta相关映射属性
_YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
if (!propertyMeta) return;
//属性元数组中移除存在映射属性的PropertyMetas
[allPropertyMetas removeObjectForKey:propertyName];
//当mappedToKey是字符串类型
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;
}
}
//如果keyPath的count大于1,设置属性元的映射keypath,并将设置了keypath的属性元添加到keypath属性元数组中
if (keyPath.count > 1) {
propertyMeta->_mappedToKeyPath = keyPath;
[keyPathPropertyMetas addObject:propertyMeta];
}
//如果多个propertyMeta的key一样,我们使用链表来连接
propertyMeta->_next = mapper[mappedToKey] ?: nil;
//设置mapper字典
mapper[mappedToKey] = propertyMeta;
} 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];
}
//根据属性映射mappedToKey是否设置来进行mappedToKey和mapedToKeyPaths设置
if (!propertyMeta->_mappedToKey) {
propertyMeta->_mappedToKey = oneKey;
propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
}
}
if (!propertyMeta->_mappedToKey) return;
//设置mappedToKeyArray属性
propertyMeta->_mappedToKeyArray = mappedToKeyArray;
//将多映射属性元添加到multikeyspropertymetas数组中
[multiKeysPropertyMetas addObject:propertyMeta];
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
}
}];
}
//对于没有设置映射的属性,我们将mappedToKey设置为对应的属性名
[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;
以上就将属性元的映射key和映射keypath设置完成,同时也将存在keypath的属性元和存在多个映射key的属性元添加进入对应的数组。
YYModel是如何根据YYModelMeta解析Dic
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
//字典检测
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
//根据类对象获取到对应的YYModelMeta
_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;
}
//拼装上下文,为后续的回调函数传递参数
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
//根据模型的属性个数和字典中key的个数比较来选择对应循环模型或者字典进行赋值操作
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
//字典中key少,则循环字典进行回调函数处理
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
//由于在执行ModelSetWithDictionaryFunction的时候,寻找对应的YYModelPropertyMeta的时候使用的是map中的key,而map中的key可能存在字符串、数数组,而dic中的key肯定是字符串,所以对于map中的key为数组的propertyMeta是没有处理的。
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;
}
其中void CFArrayApplyFunction(CFArrayRef theArray, CFRange range, CFArrayApplierFunction CF_NOESCAPE applier, void *context);中,theArray表示要应用的数组,即_YYModelPropertyMetas数组。range表示应用的数组范围。applier表示数组中每个元素要调用的方法,这个方法就是字典中的字段设置到model中属性的一个函数。context表示上下文环境,即上文中创建的context,通过这个context可以获取到反转的字典等信息。
对于ModelSetWithPropertyMetaArrayFunction的实现,如下:
static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {
//将_context转化为ModelSetContext类型
ModelSetContext *context = _context;
//从context中获取到转化的字典
__unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);
//赋值通过属性的setter方法,所以判断setter是否存在
if (!propertyMeta->_setter) return;
id value = nil;
//开始获取到属性对应的值
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) {
__unsafe_unretained id model = (__bridge id)(context->model);
ModelSetValueForProperty(model, value, propertyMeta);
}
}
我们开始分析下如何获取到属性对应的值:
3. YYModel如何保证代码的侵入性呢?
使用分类,实现了方法写入类方法列表的前面,防止了代码的侵入。
4. YYModel如何达到高性能呢?
- 使用CFDictionaryCreateMutable创建类缓存和元模型对象缓存,减少了计算类信息的次数,从而优化了性能
- 使用信号量,由于在信号量中操作的代码款是不需要等待的,所以信号量的性能相对是很高的。
- 对模型进行赋值的过程中,使用基于Core foundation底层数据结构,优化了性能。
- 在设置值的过程,使用属性的setter方法,而避免使用kvc,优化了性能。
问题点分析:
1. YYEncodingGetType(attrs[i].value);怎么理解?
2. 获取容器映射类的时候,为什么要用获取类的元类信息?
相关总结点:
1. 对于copy和mutableCopy操作是否创建新的内存空间,我们只需要理解copy操作是为了实现两个对象不相互影响的。所以可以总结为一下:
修改新对象(旧)对象,不影响旧对象(新)对象。如果旧对象是不可修改,则使用copy的话,系统会根据就对象的不可修改优化内存,从而产生浅拷贝,而如果对于旧对象的mutablecopy操作,则会导致新对象是可修改的,从而使得系统会创建新的内存空间,从而不相互影响。
- object_getClass(obj)和[xxx class]的区别:
使用object_getClass返回的是对象的isa指针指向的类,而使用class则分情况:
我们查看下[obj class]的源码
//对于使用了object_getClass返回的是对象的isa指针,即对象指向的类
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
//如果调用的是类的方法或者元类或者元类的类的class,则返回的是当前自己
+ (Class)class {
return self;
}
//对于是类的对象是类的实例,调用的是实例方法,返回的是object_getClass指向的类指针。
- (Class)class {
return object_getClass(self);
}
- CFArrayApplyFunction的作用?
首先我们看下函数原型:
//其中theArray是要应用的数组,CFArrayApplierFunction表示每个元素要执行的函数,context表示执行的上下文,即YYModel中设置的context。
void CFArrayApplyFunction(CFArrayRef theArray, CFRange range, CFArrayApplierFunction CF_NOESCAPE applier, void *context)
我们来看下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;
//针对属性元中的映射key分别使用不同的方式获取key对应的值
if (propertyMeta->_mappedToKeyArray) {
value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
} else if (propertyMeta->_mappedToKeyPath) {
value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
} else {
value = [dictionary objectForKey:propertyMeta->_mappedToKey];
}
//如果值存在,那就可以开始使用ModelSetValueForProperty来给属性设置值了
if (value) {
__unsafe_unretained id model = (__bridge id)(context->model);
ModelSetValueForProperty(model, value, propertyMeta);
}
}
我们看下ModelSetValueForProperty(model, value, propertyMeta);的源码:
static void ModelSetValueForProperty(__unsafe_unretained id model,
__unsafe_unretained id value,
__unsafe_unretained _YYModelPropertyMeta *meta) {
//判断属性是不是number类型,如果是则执行
if (meta->_isCNumber) {
NSNumber *num = YYNSNumberCreateFromID(value);
ModelSetNumberToProperty(model, num, meta);
if (num != nil) [num class]; // hold the number
} else if (meta->_nsType) {
if (value == (id)kCFNull) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
} else {
switch (meta->_nsType) {
case YYEncodingTypeNSString:
case YYEncodingTypeNSMutableString: {
if ([value isKindOfClass:[NSString class]]) {
if (meta->_nsType == YYEncodingTypeNSString) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy);
}
} else if ([value isKindOfClass:[NSNumber class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
(meta->_nsType == YYEncodingTypeNSString) ?
((NSNumber *)value).stringValue :
((NSNumber *)value).stringValue.mutableCopy);
.............
}
对于赋值的过程使用了objc_msgSend执行setter函数。
为什么不使用kvc而使用setter进行赋值操作呢?
根据kvc的搜索逻辑,我们知道针对赋值操作需要一连串的查询逻辑,比较消耗性能。而YYModel则先判断setter函数是否存在,而后直接使用objc_msgSend进行函数调用赋值,这样减少了kvc的查询逻辑,提高了整体的性能。
YYModel中应用的知识点总结:
1. 运用runtime知识获取类的变量、属性、方法。对于属性的类型编码和方法的参数编码,运用编码对照表一一对照。
运用信号量实现线程安全:
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
需要同步的操作
DISPATCH_SEMAPHORE_SIGNAL(lock)使用CFDictionaryCreateMutable创建字典缓存,缓存封装好的YYClassInfo、YYModelMeta信息,因为这类信息在一般情况下不会改变,同时使用的次数会比较多,通过缓存可以减少创建的次数,优化了性能。
在使用字典或者数组循环处理的地方,使用CFDictionaryApplyFunction或者CFDictionaryApplyFunction。优化性能。
使用objc_msgSend为属性赋值,而避免使用kvc操作,优化了性能