YYModel源码解析
文章目录
前言
最近学习一些有关于第三方库源码的内容,把YYModel和JSONModel这两个第三方库的内容看完
YYModel性能优势
YYModel 是一个非常轻量级的 JSON 模型自动转换库,代码风格良好且思路清晰,可以从源码中看到作者对 Runtime 深厚的理解。难能可贵的是 YYModel 在其轻量级的代码下还保留着自动类型转换,类型安全,无侵入等特性,并且具有接近手写解析代码的超高性能。
YYModel简介
这里其实我们可以看出YYModel的结构其实是比较简单的,其实只有YYClassInfo
和NSObject+YYModel
两个模块
- 前者将Runtime层级的一些结构体封装到NSObject层以便调用
- 后者负责提供方便调用的接口以及实现具体的模型转换逻辑
YYClassInfo解析
这里我们看一张图来对比理解这里的YYClassInfo
和Runtime
层级的一些结构体的关系:
YYClassInfo | Runtime |
---|---|
YYClassIvarInfo | objc_ivar |
YYClassMethodInfo | objc_method |
YYClassPropertyInfo | property_t |
YYClassInfo | objc_class |
下面我会一一对比两者直接的关系:
YYClassIvarInfo && objc_ivar
@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 变量类型,通过YYEncodingGetType方法从类型编码中取得
/**
Creates and returns an ivar info object.
@param ivar ivar opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithIvar:(Ivar)ivar;
@end
//对应的objc_var
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
}
这里我们发现这里的NSString
类型都被strong
来修饰,因为所有的字符串都是通过Runtime方法拿到的const char *
之后通过stringWithUTF8String
方法转化成NSString的,这里使用strong做一次单纯的强引用在性能上会比copy高
YYClassMethodInfo && objc_method
@interface YYClassMethodInfo : NSObject
@property (nonatomic, assign, readonly) Method method; ///< method opaque struct // 方法
@property (nonatomic, strong, readonly) NSString *name; ///< method name // 方法名
@property (nonatomic, assign, readonly) SEL sel; ///< method's selector //sel
@property (nonatomic, assign, readonly) IMP imp; ///< method's implementation //IMP指针
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types // 方法参数和方法类型
@property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type // 返回值类型编码
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type // 参数类型编码
//源码
struct objc_method {
SEL _Nonnull method_name OBJC2_UNAVAILABLE; // 方法名称
char * _Nullable method_types OBJC2_UNAVAILABLE; // 方法类型
IMP _Nonnull method_imp OBJC2_UNAVAILABLE; // 方法实现(函数指针)
}
sel 可以理解为方法选择器,也就是映射到方法的C字符串
imp 也就是指向方法具体实现逻辑的函数指针
YYClassPropertyInfo && property_t
@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;
@end
struct property_t {
const char *name; // 名称
const char *attributes; // 修饰
};
可以理解成对于YYClassPropertyInfo
是作者对于property_t
封装,注意一下对于getter和setter方法的封装
//先尝试获取属性的getter和setter方法
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
// 如果没有则按照标准规则自己造
if (!_getter) {
_getter = NSSelectorFromString(_name);
}
if (!_setter) {
_setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
}
YYClassInfo && objc_class
最后YYModel还封装了objc_class
,objc_class
在Runtime中表示一个OC类.
@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
// 源码部分
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;
这里可以看到他对于每一个类对象里面所有内容进行封装.
上面简单介绍了一下这几个核心结构体和Runtime的底层的对于关系,以及一个处理
YYClassInfo的初始化细节
我们这里是把objc_class
在外层封装成一个YYClassInfo
,这里就简单看一下这个初始化函数
+ (instancetype)classInfoWithClass:(Class)cls {
// 判空入参
if (!cls) return nil;
// 单例缓存 classCache 与 metaCache,对应缓存类和元类
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);
// 这里把 dispatch_semaphore 当做锁来使用(当信号量只有 1 时)
lock = dispatch_semaphore_create(1);
});
// 初始化之前,首先会根据当前 YYClassInfo 是否为元类去对应的单例缓存中查找
// 这里使用了上面的 dispatch_semaphore 加锁,保证单例缓存的线程安全
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);
// 根据初始化信息选择向对应的类/元类缓存注入信息,key = cls,value = info
CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
dispatch_semaphore_signal(lock);
}
}
return info;
}
- 创建单利缓存,类缓存和元类缓存
- 使用
信号量(dispatch_semaphore)
作为锁保证缓存线程安全 - 初始化前先去缓存中查找是否已经象缓存中注册当前要初始化的YYClassInfo
- 如果查找到缓存对象,则判断缓存对象是否需要更新并执行相关操作
- 如果缓存中为找到缓存对象则初始化
- 初始化完成后向缓存中注册
YYClassInfo
实例
NSObject+YYModel 探究
这里是利用YYClassInfo层级封装好的类切实的执行JSON模型的有一个转换逻辑,并且提供类一个无侵入性的接口,主要分成了四个模块实现对于JSON的逻辑转换
- 类型编码解析
- 数据结构定义
- 递归模型转换
- 接口相关代码
类型编码解析
类型编码解析代码主要集中在 NSObject+YYModel.m 的上面部分,涉及到 YYEncodingNSType
枚举的定义,配套 YYClassGetNSType
函数将 NS 类型转为 YYEncodingNSType 还有 YYEncodingTypeIsCNumber
函数判断类型是否可以直接转为 C 语言数值类型的函数。
typedef NS_ENUM (NSUInteger, YYEncodingNSType) {
YYEncodingTypeNSUnknown = 0,
YYEncodingTypeNSString,
YYEncodingTypeNSMutableString,
YYEncodingTypeNSValue,
YYEncodingTypeNSNumber,
YYEncodingTypeNSDecimalNumber,
YYEncodingTypeNSData,
YYEncodingTypeNSMutableData,
YYEncodingTypeNSDate,
YYEncodingTypeNSURL,
YYEncodingTypeNSArray,
YYEncodingTypeNSMutableArray,
YYEncodingTypeNSDictionary,
YYEncodingTypeNSMutableDictionary,
YYEncodingTypeNSSet,
YYEncodingTypeNSMutableSet,
};
static force_inline YYEncodingNSType YYClassGetNSType(Class cls) {
if (!cls) return YYEncodingTypeNSUnknown;
if ([cls isSubclassOfClass:[NSMutableString class]]) return YYEncodingTypeNSMutableString;
if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;
if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return YYEncodingTypeNSDecimalNumber;
if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;
if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;
if ([cls isSubclassOfClass:[NSMutableData class]]) return YYEncodingTypeNSMutableData;
if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;
if ([cls isSubclassOfClass:[NSDate class]]) return YYEncodingTypeNSDate;
if ([cls isSubclassOfClass:[NSURL class]]) return YYEncodingTypeNSURL;
if ([cls isSubclassOfClass:[NSMutableArray class]]) return YYEncodingTypeNSMutableArray;
if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;
if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return YYEncodingTypeNSMutableDictionary;
if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;
if ([cls isSubclassOfClass:[NSMutableSet class]]) return YYEncodingTypeNSMutableSet;
if ([cls isSubclassOfClass:[NSSet class]]) return YYEncodingTypeNSSet;
return YYEncodingTypeNSUnknown;
}
//这两个部分的内容实现将cls快速转化成枚举值,方便后面对于不同类型进行一个处理
//认识一下inline这个内容:主要就是编译器将函数体直接插入调用出,而不是生成函数调用,减小函数调用的时候的开销,就相当于减少了一个函数栈帧插入的一个过程
这里我们看一下有关于将YYNSBlockClass
指向了NSBlock
类,实现过程如下:
static force_inline Class YYNSBlockClass() {
static Class cls;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
void (^block)(void) = ^{}; // 创建一个空的Block
cls = ((NSObject *)block).class; //获得指针
while (class_getSuperclass(cls) != [NSObject class]) { // 向上查找
cls = class_getSuperclass(cls);
}
});
return cls; // current is "NSBlock"
}
// block的类结构大致如下:
__NSGlobalBlock__
↓
__NSBlock__ // 到这一层就不再查找,退出,然后给他赋值给我们的YYNSBlockClass执行NSBlock
↓
NSObject
数据结构的定义
NSObject+YYModel 中重新定义了两个类,通过它们来使用 YYClassInfo 中的封装。
NSObject+YYModel | YYClassInfo |
---|---|
_YYModelPropertyMeta |
YYClassPropertyInfo |
_YYModelMeta |
YYClassInfo |
_YYModelPropertyMeta
这里是一个模型对象中的属性信息,它包含YYClassPropertyInfo
@interface _YYModelPropertyMeta : NSObject {
@package
NSString *_name; ///< property's name 属性名称
YYEncodingType _type; ///< property's type 属性类型
YYEncodingNSType _nsType; ///< property's Foundation type 框架中的类型
BOOL _isCNumber; ///< is c number type 是否为CNumber, 也就是判断是不是一个基本的类型
Class _cls; ///< property's class, or nil 属性类
Class _genericCls; ///< container's generic class, or nil if threr's no generic class 属性包含的泛型类型,没有则为 nil
SEL _getter; ///< getter, or nil if the instances cannot respond getter方法
SEL _setter; ///< setter, or nil if the instances cannot respond setter方法
BOOL _isKVCCompatible; ///< YES if it can access with key-value coding 如果可以使用KVC就可以返回YES
BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver // 如果可以使用 archiver/unarchiver 归/解档则返回 YES
BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:
// 类/泛型自定义类型,例如需要在数组中实现不同类型的转换需要用到
/*
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; ///< the key mapped to 映射key
NSArray *_mappedToKeyPath; ///< the key path mapped to (nil if the name is not key path) 映射keyPath则返回nil
NSArray *_mappedToKeyArray; ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys) key或者keyPath数组,如果没有映射多个key的话则返回nil
YYClassPropertyInfo *_info; ///< property's info 属性信息
_YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key. //如果有多个属性映射到同一个key 则执行下一个模型属性元
}
_YYModelMeta
这里表示的是模型对象中的属性信息:
@interface _YYModelMeta : NSObject {
@package
YYClassInfo *_classInfo;
/// Key:mapped key and key path, Value:_YYModelPropertyMeta.
NSDictionary *_mapper; // 当前字典的KEY 与KeyPath Value:_YYModelPropertyMeta的内容
/// Array<_YYModelPropertyMeta>, all property meta of this model.
NSArray *_allPropertyMetas; // 当前Model所有的 _YYModelPropertyMeta
/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
NSArray *_keyPathPropertyMetas; // 被映射到 keyPath 的 _YYModelPropertyMeta 数组
/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
NSArray *_multiKeysPropertyMetas; // 被映射到 key 的 _YYModelPropertyMeta 数组
/// The number of mapped key (and key path), same to _mapper.count.
NSUInteger _keyMappedCount; // key 与keyPath的数量
/// Model class type.
YYEncodingNSType _nsType; // class的类型
BOOL _hasCustomWillTransformFromDictionary;
BOOL _hasCustomTransformFromDictionary;
BOOL _hasCustomTransformToDictionary;
BOOL _hasCustomClassFromDictionary;
}
JSON->Model
这里我们从转化的起点开始学习
+ (instancetype)yy_modelWithJSON:(id)json {
// 将 json 转为字典 dic
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
// 将json转化为Model
return [self yy_modelWithDictionary:dic];
}
这里我们把这个步骤转化成了两个步骤:
JSON->Dictionary
这里我们在看一下有关于yy_dictionaryWithJSON
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {
//入参判空
if (!json || json == (id)kCFNull) return nil;
NSDictionary *dic = nil;
NSData *jsonData = nil;
if ([json isKindOfClass:[NSDictionary class]]) {
// 如果是 NSDictionary 类则直接赋值
dic = json;
} else if ([json isKindOfClass:[NSString class]]) {
// 如果是 NSString 类则用 UTF-8 编码转 NSData
jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
//如果是data直接赋值
jsonData = json;
}
if (jsonData) {
// 利用 NSJSONSerialization 方法将 jsonData 转为 dic
dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
if (![dic isKindOfClass:[NSDictionary class]]) dic = nil; // 判断转换结果
}
return dic;
}
其中 kCFNull
是 CoreFoundation 中 CFNull 的单例对象。如同 Foundation 框架中的 NSNull 一样,CFNull 是用来表示集合对象中的空值(不允许为 NULL)。CFNull 对象既不允许被创建也不允许被销毁,而是通过定义一个 CFNull 常量,即 kCFNull
,在需要空值时使用。
NSDictionary->Model
这里我们再看第二个步骤:
+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
Class cls = [self class];
// 使用当前类生成一个 _YYModelMeta 模型元类
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
// 这里 _hasCustomClassFromDictionary 用于标识是否需要自定义返回类
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
// 调用 yy_modelSetWithDictionary 为新建的类实例 one 赋值,赋值成功则返回 one
NSObject *one = [cls new];
if ([one yy_modelSetWithDictionary:dictionary]) return one;
return nil;
}
这里之后再进入下面这个方法yy_modelSetWithDictionary
这个方法可以说是模型转化的核心代码:
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
//如果key没有就返回NO
if (modelMeta->_keyMappedCount == 0) return NO;
//是否有 modelCustomWillTransformFromDictionary 接口
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);
// 判断模型元键值映射数量与 JSON 所得字典的数量关系, 将dictionary的数据填充到我们的Model层上面
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
// 一般情况下他们的数量相等
// 特殊情况比如有的属性元会映射字典中的多个 key
// 为字典中的每个键值对调用 ModelSetWithDictionaryFunction
// 这句话是核心代码,一般情况下就是靠 ModelSetWithDictionaryFunction 通过字典设置模型
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
// 处理keyPath的属性
if (modelMeta->_keyPathPropertyMetas) {
//对每一个keypath调用对应方法
CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
//处理映射多个 key 的属性元
if (modelMeta->_multiKeysPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
} else {
//处理单个key的方法
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
这里主要分成了下面几个步骤:
- 入参校验
- 初始化模型元以及映射表校验
- 初始化模型设置上下文
ModelSetContext
- 为字典中的每个键值对调用
ModelSetWithDictionaryFunction
- 检验转换结果
上下文的结构体如下图:
typedef struct {
void *modelMeta; ///< 模型元
void *model; ///< 模型实例,指向输出的模型
void *dictionary; ///< 待转换字典
} ModelSetContext;
下面在看一下这个方法:ModelSetWithDictionaryFunction
:这一步可以理解为设置Model的逻辑
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)];
//获得对应的一个未处理的model
__unsafe_unretained id model = (__bridge id)(context->model);
// 遍历 propertyMeta,直到 propertyMeta->_next == nil
while (propertyMeta) {
if (propertyMeta->_setter) { //如果有setter方法就采用setter,则调用 ModelSetValueForProperty 赋值
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
}
propertyMeta = propertyMeta->_next;
};
}
这个方法就是通过获取对应的一个属性元,找到 setter
不为空的属性元通过 ModelSetValueForProperty
方法赋值。
ModelSetValueForProperty
函数是为模型中的属性赋值的实现方法,也是整个 YYModel 的核心代码
static void ModelSetValueForProperty(__unsafe_unretained id model,
__unsafe_unretained id value,
__unsafe_unretained _YYModelPropertyMeta *meta) {
if (meta->_isCNumber) {
NSNumber *num = YYNSNumberCreateFromID(value);
ModelSetNumberToProperty(model, num, meta);
if (num) [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);
} 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;
case YYEncodingTypeNSValue:
case YYEncodingTypeNSNumber:
case YYEncodingTypeNSDecimalNumber: {
if (meta->_nsType == YYEncodingTypeNSNumber) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSNumberCreateFromID(value));
} else if (meta->_nsType == YYEncodingTypeNSDecimalNumber) {
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;
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]]) {
((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]]) {
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;
case YYEncodingTypeNSArray:
case YYEncodingTypeNSMutableArray: {
if (meta->_genericCls) {
NSArray *valueArr = nil;
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]) {
[objectArr addObject:one];
} else if ([one isKindOfClass:[NSDictionary class]]) {
Class cls = meta->_genericCls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:one];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
NSObject *newOne = [cls new];
[newOne yy_modelSetWithDictionary:one];
if (newOne) [objectArr addObject:newOne];
}
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);
}
} else {
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]]) {
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 yy_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;
case YYEncodingTypeNSSet:
case YYEncodingTypeNSMutableSet: {
NSSet *valueSet = nil;
if ([value isKindOfClass:[NSArray class]]) valueSet = [NSMutableSet setWithArray:value];
else if ([value isKindOfClass:[NSSet class]]) valueSet = ((NSSet *)value);
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) {
cls = [cls modelCustomClassForDictionary:one];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
NSObject *newOne = [cls new];
[newOne yy_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 yy_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 yy_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: {
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;
case YYEncodingTypeStruct:
case YYEncodingTypeUnion:
case YYEncodingTypeCArray: {
if ([value isKindOfClass:[NSValue class]]) {
const char *valueType = ((NSValue *)value).objCType;
const char *metaType = meta->_info.typeEncoding.UTF8String;
if (valueType && metaType && strcmp(valueType, metaType) == 0) {
[model setValue:value forKey:meta->_name];
}
}
} break;
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;
if (nsValue.objCType && strcmp(nsValue.objCType, "^v") == 0) {
((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, nsValue.pointerValue); // 最后都会采用setter方法来给数据赋值
}
}
} // break; commented for code coverage in next line
default: break;
}
}
}
这个函数就是通过判断属性类型来对于model层的数据进行一个赋值操作
- 根据属性元类型划分代码逻辑
- 如果属性元是 CNumber 类型,即 int、uint 之类,则使用
ModelSetNumberToProperty
赋值 - 如果属性元属于 NSType 类型,即 NSString、NSNumber 之类,则根据类型转换中可能涉及到的对应类型做逻辑判断并赋值(可以去上面代码中查看具体实现逻辑)
- 如果属性元不属于 CNumber 和 NSType,则猜测为 id,Class,SEL,Block,struct、union、char[n],void或 char 类型并且做出相应的转换和赋值
这里最后都是采用有一个我们的属性的setter方法来调用的
这里有一位大佬给出了一个图:
Model->JSON
我们有从Model转向JSON那自然也有从JSON转向Model的内容,这里看这个函数:
- (id)yy_modelToJSONObject {
/*
Apple said:
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
*/
id jsonObject = ModelToJSONObjectRecursive(self);
if ([jsonObject isKindOfClass:[NSArray class]]) return jsonObject;
if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject;
return nil;
}
这里因为是采用的是NSJSONSerialization
来进行一个转化的,将一个对象model
转化成JSON数据
.他有一个下面的要求
顶级对象是 NSArray 或者 NSDictionary 类型
所有的对象都是 NSString, NSNumber, NSArray, NSDictionary, 或 NSNull 的实例
所有字典中的 key 都是一个 NSString 实例
Numbers 是除去无穷大和 NaN 的其他表示
这里我们简单看一下这个函数:这不罗列出详细的内容,简单介绍一下相关内容,这里是对于Model层的每一个元素进行一个递归检查的内容,
// 递归转换模型到 JSON,如果转换异常则返回 nil
static id ModelToJSONObjectRecursive(NSObject *model) {
// 判空或者可以直接返回的对象,则直接返回
if (!model || model == (id)kCFNull) return model;
if ([model isKindOfClass:[NSString class]]) return model;
if ([model isKindOfClass:[NSNumber class]]) return model;
// 如果 model 从属于 NSDictionary
if ([model isKindOfClass:[NSDictionary class]]) {
// 如果可以直接转换为 JSON 数据,则返回
if ([NSJSONSerialization isValidJSONObject:model]) return model;
NSMutableDictionary *newDic = [NSMutableDictionary new];
// 遍历 model 的 key 和 value
[((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;
if (!stringKey) return;
// 递归解析 value
id jsonObj = ModelToJSONObjectRecursive(obj);
if (!jsonObj) jsonObj = (id)kCFNull;
newDic[stringKey] = jsonObj;
}];
return newDic;
}
// 如果 model 从属于 NSSet
if ([model isKindOfClass:[NSSet class]]) {
// 如果能够直接转换 JSON 对象,则直接返回
// 否则遍历,按需要递归解析
...
}
if ([model isKindOfClass:[NSArray class]]) {
// 如果能够直接转换 JSON 对象,则直接返回
// 否则遍历,按需要递归解析
...
}
// 对 NSURL, NSAttributedString, NSDate, NSData 做相应处理
if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model];
if ([model isKindOfClass:[NSData class]]) return nil;
// 用 [model class] 初始化一个模型元
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];
// 如果映射表为空,则不做解析直接返回 nil
if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil;
// 性能优化细节,使用 __unsafe_unretained 来避免在下面遍历 block 中直接使用 result 指针造成的不必要 retain 与 release 开销
NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64];
__unsafe_unretained NSMutableDictionary *dic = result;
// 遍历模型元属性映射字典
[modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
// 如果遍历当前属性元没有 getter 方法,跳过
if (!propertyMeta->_getter) return;
id value = nil;
// 如果属性元属于 CNumber,即其 type 是 int、float、double 之类的
if (propertyMeta->_isCNumber) {
// 从属性中利用 getter 方法得到对应的值
value = ModelCreateNumberFromProperty(model, propertyMeta);
} else if (propertyMeta->_nsType) { // 属性元属于 nsType,即 NSString 之类
// 利用 getter 方法拿到 value
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
// 对拿到的 value 递归解析
value = ModelToJSONObjectRecursive(v);
} else {
// 根据属性元的 type 做相应处理
switch (propertyMeta->_type & YYEncodingTypeMask) {
// id,需要递归解析,如果解析失败则返回 nil
case YYEncodingTypeObject: {
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = ModelToJSONObjectRecursive(v);
if (value == (id)kCFNull) value = nil;
} break;
// Class,转 NSString,返回 Class 名称
case YYEncodingTypeClass: {
Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = v ? NSStringFromClass(v) : nil;
} break;
// SEL,转 NSString,返回给定 SEL 的字符串表现形式
case YYEncodingTypeSEL: {
SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = v ? NSStringFromSelector(v) : nil;
} break;
default: break;
}
}
// 如果 value 还是没能解析,则跳过
if (!value) return;
// 当前属性元是 KeyPath 映射,即 a.b.c 之类
if (propertyMeta->_mappedToKeyPath) {
NSMutableDictionary *superDic = dic;
NSMutableDictionary *subDic = nil;
// _mappedToKeyPath 是 a.b.c 根据 '.' 拆分成的字符串数组,遍历 _mappedToKeyPath
for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) {
NSString *key = propertyMeta->_mappedToKeyPath[i];
// 遍历到结尾
if (i + 1 == max) {
// 如果结尾的 key 为 nil,则使用 value 赋值
if (!superDic[key]) superDic[key] = value;
break;
}
// 用 subDic 拿到当前 key 对应的值
subDic = superDic[key];
// 如果 subDic 存在
if (subDic) {
// 如果 subDic 从属于 NSDictionary
if ([subDic isKindOfClass:[NSDictionary class]]) {
// 将 subDic 的 mutable 版本赋值给 superDic[key]
subDic = subDic.mutableCopy;
superDic[key] = subDic;
} else {
break;
}
} else {
// 将 NSMutableDictionary 赋值给 superDic[key]
// 注意这里使用 subDic 间接赋值是有原因的,原因就在下面
subDic = [NSMutableDictionary new];
superDic[key] = subDic;
}
// superDic 指向 subDic,这样在遍历 _mappedToKeyPath 时即可逐层解析
// 这就是上面先把 subDic 转为 NSMutableDictionary 的原因
superDic = subDic;
subDic = nil;
}
} else {
// 如果不是 KeyPath 则检测 dic[propertyMeta->_mappedToKey],如果为 nil 则赋值 value
if (!dic[propertyMeta->_mappedToKey]) {
dic[propertyMeta->_mappedToKey] = value;
}
}
}];
// 忽略,对应 modelCustomTransformToDictionary 接口
if (modelMeta->_hasCustomTransformToDictionary) {
// 用于在默认的 Model 转 JSON 过程不适合当前 Model 类型时提供自定义额外过程
// 也可以用这个方法来验证转换结果
BOOL suc = [((id<YYModel>)model) modelCustomTransformToDictionary:dic];
if (!suc) return nil;
}
return result;
}
代码还是有些长,这里笔者简单讲一下这里的内容,简单总结一下上面的代码逻辑:
- 判断入参,如果满足条件可以直接返回
- 如果Model从属于NSType,根据不同的类型做逻辑处理
- 如果上面条件不被满足,则用Model的Class初始化一个模型元
_YYModelMeta
- 判断模型元的映射关系,遍历映射拿到对应的键值存入字典中返回
这里有一个性能优化的细节,用__unsafe_unretained
修饰的dic指向我们最后要return的NSMutableDictionary *result
.
这里作者大篇幅的采用来unsafe_unretained
来处理循环引用的内容,这里就可以减少很多不需要的retain
和release
的操作,因为这样可以提高速度,
总结
这里笔者学习YYModel主要是为了认识为什么YYModel有更多的性能优势?
- 首先YYModel十分轻量级,它将Runtime的底层结构体封装了成了方便调用的结构体,减少来代码逻辑
- 同时YYModel中频繁调用了Core Foundation框架中的内容,
Core Foundation
方法在调用的时候远远优与Foundation
中的方法 - 同时在YYModel中经常使用纯C函数,避免了消息发送的开销
- 还有与JSONModel最有区别的一点就是YYModel中使用
setter
方法进行了value值的设置而非KVC
,这样也提高了性能 - YYModel采用了分类,实现了对于Model的无入侵性,这点比较重要