最近感觉知识匮乏,成长进步进度缓慢,早就听说YYKit屌到不行,遂来试着读一下,第一个先看YYModel
首先看一下 YYClassInfo这个类,里面实现了对class基本信息的封装 (先大概了解,后面细说)
YYClassIvarInfo,YYClassMethodInfo,YYClassPropertyInfo,YYClassInfo
接下来说一下YYModel的使用(通过一个字典返回该类的实例化对象)注:并不是全部的代码,有些跟逻辑无关的东西省略掉了。
+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary {
Class cls = [self class];
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];//获取该类的信息
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
NSObject *one = [cls new]; // 创建该类的实例对象,并赋值
if ([one modelSetWithDictionary:dictionary]) return one;
return nil;
}
有两个关键点是_YYModelMeta and modelSetWithDictionary:
我们一一来看:
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef cache;//类信息缓存,是CoreFundation里面的字典(不要被这种名字唬住,使用起来是一样的,可能性能会稍微高一点儿)
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;//这个是dispatch里面的信号量,用来限制同时访问该地方的线程数量,通过dispatch_semaphore_create(1)来限制只有一条线程可以访问,类似线程加锁
dispatch_once(&onceToken, ^{ // 只执行一次。(通过这一系列操作,实现了类似单例的功能,用来缓存不同的类信息)
// 类信息缓存初始化
cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);// dispatch信号量的具体使用
_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 alloc] initWithClass:cls]; 该类的构造方法都做了什么
- (instancetype)initWithClass:(Class)cls {
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];//这里是真正的类信息对象
// 黑名单
NSSet *blacklist = nil;
if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
NSArray *properties = [(id)cls modelPropertyBlacklist];
if (properties) {
blacklist = [NSSet setWithArray:properties];
}
}
// 白名单
NSSet *whitelist = nil;
if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
NSArray *properties = [(id)cls modelPropertyWhitelist];
if (properties) {
whitelist = [NSSet setWithArray:properties];
}
}
// 如果该model变量里有其他类的对象,例{@"son":[Son class]}
NSDictionary *genericMapper = nil;
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
}
// 这个是获取该类所有属性信息(包括父类)
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
while (curClassInfo && curClassInfo.superCls != nil) {
curClassInfo = curClassInfo.superClassInfo;
}
// 这个是用户自定义了映射关系 还有keypath的处理
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {}
return self;
}
上面删减了大部分的代码,因为该方法篇幅太大,而且使用到了一些还没具体讲到的类和方法,目前我们大概了解到
_YYModelMeta该类的实例对象包含了一个 YYClassInfo对象(类信息对象),还有黑白名单的处理,获取所有父类属性,以及对keypath还有用户自定义映射的处理。(若想了解具体实现参阅源码)
接下来讲 YYClassInfo
该类也有个类方法 classInfoWithClass: 具体实现跟_YYModelMeta的相仿,需要注意的是有一个元类信息的缓存- metaCache。(以下是代码,不用细看,跟_YYModelMeta几乎一样)
对于元类不了解的同学可以看下我的这篇文章类探究-isa
+ (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);
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) {
[info _update];
}
dispatch_semaphore_signal(lock);
if (!info) {
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;
}
接下来看看 YYClassInfo的构造方法initWithClass:
- (instancetype)initWithClass:(Class)cls {
_cls = cls;// 本类
_superCls = class_getSuperclass(cls); //父类
_isMeta = class_isMetaClass(cls); // 是否是元类
if (!_isMeta) {
_metaCls = objc_getMetaClass(class_getName(cls));// 如果不是元类,获取元类
}
_name = NSStringFromClass(cls);
[self _update]; // 获取该类的method property ivar的信息
_superClassInfo = [self.class classInfoWithClass:_superCls]; //获取父类的类信息
return self;
}
- (void)_update {
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++) {
YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];
if (info.name) methodInfos[info.name] = info;
}
free(methods);
}
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);
}
unsigned int ivarCount = 0;
Ivar *ivars = class_copyIvarList(cls, &ivarCount); // 获取ivar信息
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);
}
if (!_ivarInfos) _ivarInfos = @{};
if (!_methodInfos) _methodInfos = @{};
if (!_propertyInfos) _propertyInfos = @{};
_needUpdate = NO;
}
通过上面代码大概了解到该类包含了本类和父类的类信息,是否是元类,或者自己的元类,以及该类所有的method,property,ivar信息,这里涉及到三个类YYClassMethodInfo、YYClassPropertyInfo、YYClassIvarInfo来一一说下
- (instancetype)initWithMethod:(Method)method {
if (!method) return nil;
self = [super init];
_method = method;
_sel = method_getName(method); // 获取SEL
_imp = method_getImplementation(method); // 获取方法的实现IMP
const char *name = sel_getName(_sel); // 获取方法名称
if (name) {
_name = [NSString stringWithUTF8String:name];
}
const char *typeEncoding = method_getTypeEncoding(method);//返回该方法的编码 例如setCenterPlaceholder: 编码以后返回-> v20@0:8B16
if (typeEncoding) {
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
}
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++) {
char *argumentType = method_copyArgumentType(method, i); // 获取参数类型
NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
[argumentTypes addObject:type ? type : @""];
if (argumentType) free(argumentType);
}
_argumentTypeEncodings = argumentTypes;
}
return self;
}
- (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]) {
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': { // Instance variable
if (attrs[i].value) {
_ivarName = [NSString stringWithUTF8String:attrs[i].value];
}
} break;
case 'R': {
type |= YYEncodingTypePropertyReadonly;
} break;
case 'C': {
type |= YYEncodingTypePropertyCopy;
} break;
case '&': {
type |= YYEncodingTypePropertyRetain;
} break;
case 'N': {
type |= YYEncodingTypePropertyNonatomic;
} break;
case 'D': {
type |= YYEncodingTypePropertyDynamic;
} break;
case 'W': {
type |= YYEncodingTypePropertyWeak;
} break;
case 'G': {
type |= YYEncodingTypePropertyCustomGetter;
if (attrs[i].value) {
_getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} break;
case 'S': {
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;
}
- (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];
}
_offset = ivar_getOffset(ivar);
const char *typeEncoding = ivar_getTypeEncoding(ivar);
if (typeEncoding) {
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
_type = YYEncodingGetType(typeEncoding);
}
return self;
}
接下来回到了这里 NSObject *one = [cls new]; if ([one modelSetWithDictionary:dictionary]) return one; (创建类实例对象,并赋值Dictionary)
- (BOOL)modelSetWithDictionary:(NSDictionary *)dic { // 初始化实例对象后的赋值操作
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)]; // 该方法介绍过 获取类信息 ,有缓存机制
ModelSetContext context = {0}; // 这是一个简单的结构体 包含以下三个变量 方便方法传参的时候使用
context.modelMeta = (__bridge void *)(modelMeta); //类信息
context.model = (__bridge void *)(self); //当前实例对象
context.dictionary = (__bridge void *)(dic); // 赋值的字典
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) { // 比较json的数据条目多,还是类的属性多,谁少就遍历谁,非常机智。。。
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context); //此方法为CFFoundation的字典遍历 参数分别为要遍历的字典, 遍历使用的方法, 传入的参数
if (modelMeta->_keyPathPropertyMetas) { // 如果有KeyPath 类型
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 { // 如果字典的count > 对象的属性.count 那就遍历所有属性
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction, // 这个方法实现里面 包含了KeyPath 和多对一的处理
&context);
}
return YES;
}
上面可以看出比较关键赋值方法在这里
// 遍历字典
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
__unsafe_unretained id model = (__bridge id)(context->model);
while (propertyMeta) {
if (propertyMeta->_setter) {
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
}
propertyMeta = propertyMeta->_next;
};
}
这里用到一个比较关键的类_YYModelPropertyMeta,它的来由是什么呢
我们在_YYModelMeta的构造方法initWithClass:里省略了一部分内容《获取所有属性》,现在放出来(
// 这个是获取该类所有属性信息(包括父类)
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
while (curClassInfo && curClassInfo.superCls != nil) { //这里是遍历父类 但是忽略了root class (NSObject/NSProxy)
for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
// ... 夹杂一下判空,和黑白名单的
_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]];
if (!meta || !meta->_name) continue;
if (!meta->_getter || !meta->_setter) continue; //是否同时有getter和setter方法
if (allPropertyMetas[meta->_name]) continue;
allPropertyMetas[meta->_name] = meta; // 保存到字典里
}
curClassInfo = curClassInfo.superClassInfo;
}
// 以上可以知道 allPropertyMetas 里面value存的是_YYModelPropertyMeta 对象 , 那么_YYModelPropertyMeta是什么呢?
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
// 这里获取该变量是否对应的是一个对象
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;
//以下是判断该属性的类型。_type的由来在YYClassPropertyInfo构造方法里
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) {
meta->_nsType = YYClassGetNSType(propertyInfo.cls);// 这里判断是否是是以NS开头的类(详情见YYClassGetNSType方法)
} else {
meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);//这里是判断是否是NSNumber(详见YYEncodingTypeIsCNumber方法)
}
// 这里是对结构体类型的赋值语句
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) { // 如果同时拥有getter setter方法
/*
KVC invalid type:
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; // 以上类型表示可以用kvc直接复制
} break;
default: break;
}
}
return meta;
}
了解了_YYModelPropertyMeta以后 赋值方法调用到ModelSetValueForProperty() 里来
这个方法比较繁杂,所有的逻辑都在里面了,感兴趣的同学可以自行查阅源码
下面简单大概写一些运用的赋值方法
1.基本数据类型
static force_inline void ModelSetNumberToProperty(__unsafe_unretained id model,
__unsafe_unretained NSNumber *num,
__unsafe_unretained _YYModelPropertyMeta *meta) {
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool: {
((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)model, meta->_setter, num.boolValue);
} break;
... 还有好多类型
2 .NS开头的type -> 例如NSString,还要判断json里的类型,然后转化成string类型 赋值给String,可以说是很细致了。
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);
} else if ([value isKindOfClass:[NSData class]]) {
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]]) {
((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]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
(meta->_nsType == YYEncodingTypeNSString) ?
((NSAttributedString *)value).string :
((NSAttributedString *)value).string.mutableCopy);
}
} break;
总结一下:YYModel有两大核心,第一是类型转换,如何将dict里的数据转换成 model属性的类型 推荐同学们仔细看一下这两个类YYClassPropertyInfo,_YYModelPropertyMeta
第二就是类信息的缓存机制