简介
YYModel 是YY大神写的 JSON 转模型的库,其具有高性能,自动类型转换,类型安全,无侵入性,轻量等特点。
基于 Runtime 封装
OC Runtime 的数据结构如实例变量 ival, 方法 method,属性 property,类 Class 都是结构体,使用极其不便,YYmodel 对这个几个结构进行封装。
YYClassIvarInfo
此类是 OC 的 实例变量的封装
@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
/**
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
此对象,通过 Ivar 来解析出实例变量的属性,如,变量名,偏移量,类型编码,还有类型。关于类型编码您可以看苹果的官方文档https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html 和 https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html,每个类型都有相应的类型编码。而 YYEncodingType 则是 YYModel 对于实例变量的类型,修饰符的枚举类型,其定义为
typedef NS_OPTIONS(NSUInteger, YYEncodingType) {
YYEncodingTypeMask = 0xFF, ///< mask of type value
YYEncodingTypeUnknown = 0, ///< unknown
YYEncodingTypeVoid = 1, ///< void
YYEncodingTypeBool = 2, ///< bool
YYEncodingTypeInt8 = 3, ///< char / BOOL
YYEncodingTypeUInt8 = 4, ///< unsigned char
YYEncodingTypeInt16 = 5, ///< short
YYEncodingTypeUInt16 = 6, ///< unsigned short
YYEncodingTypeInt32 = 7, ///< int
YYEncodingTypeUInt32 = 8, ///< unsigned int
YYEncodingTypeInt64 = 9, ///< long long
YYEncodingTypeUInt64 = 10, ///< unsigned long long
YYEncodingTypeFloat = 11, ///< float
YYEncodingTypeDouble = 12, ///< double
YYEncodingTypeLongDouble = 13, ///< long double
YYEncodingTypeObject = 14, ///< id
YYEncodingTypeClass = 15, ///< Class
YYEncodingTypeSEL = 16, ///< SEL
YYEncodingTypeBlock = 17, ///< block
YYEncodingTypePointer = 18, ///< void*
YYEncodingTypeStruct = 19, ///< struct
YYEncodingTypeUnion = 20, ///< union
YYEncodingTypeCString = 21, ///< char*
YYEncodingTypeCArray = 22, ///< char[10] (for example)
YYEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier
YYEncodingTypeQualifierConst = 1 << 8, ///< const
YYEncodingTypeQualifierIn = 1 << 9, ///< in
YYEncodingTypeQualifierInout = 1 << 10, ///< inout
YYEncodingTypeQualifierOut = 1 << 11, ///< out
YYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy
YYEncodingTypeQualifierByref = 1 << 13, ///< byref
YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway
YYEncodingTypePropertyMask = 0xFF0000, ///< mask of property
YYEncodingTypePropertyReadonly = 1 << 16, ///< readonly
YYEncodingTypePropertyCopy = 1 << 17, ///< copy
YYEncodingTypePropertyRetain = 1 << 18, ///< retain
YYEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic
YYEncodingTypePropertyWeak = 1 << 20, ///< weak
YYEncodingTypePropertyCustomGetter = 1 << 21, ///< getter=
YYEncodingTypePropertyCustomSetter = 1 << 22, ///< setter=
YYEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic
};
可以看出这里面对所有变量类型和修饰符(nonatomic和copy等修饰)都有创建了枚举。
此类的初始化通过一个 ival 变量来解析出所有相关实例变量的属性,通过ivar_getTypeEncoding
方法来获取类型编码。然后通过YYEncodingGetType
函数来解析出 YYEncodingType 。而YYEncodingGetType
函数的思路是通过分析 typeEncoding 字符串来解析出修饰符和类型。
YYClassMethodInfo
YYClassMethodInfo 这个类则是对 runtime method的封装,里面有 method, name等属性,通过一个 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
@property (nonatomic, assign, readonly) IMP imp; ///< method's implementation
@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 *argumentTypeEncodings; ///< array of arguments' type
/**
Creates and returns a method info object.
@param method method opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithMethod:(Method)method;
@end
name 通过sel_getName
获取,sel 通过method_getName
获取,imp 通过method_getImplementation
获取,returnType 则是通过method_copyReturnType
获取,method_getNumberOfArguments
可以获取参数个数,method_copyArgumentType
可以获取每个参数的类型
YYClassPropertyInfo
YYClassPropertyInfo 是对属性 property 的封装,由一个 objc_property_t 变量来初始化
@interface YYClassPropertyInfo : NSObject
@property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct
@property (nonatomic, strong, readonly) NSString *name; ///< property's name
@property (nonatomic, assign, readonly) YYEncodingType type; ///< property's type
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value
@property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name
@property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil
@property (nullable, nonatomic, strong, readonly) NSArray *protocols; ///< may nil
@property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull)
@property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull)
/**
Creates and returns a property info object.
@param property property opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithProperty:(objc_property_t)property;
@end
name 通过property_getName
来获取,
通过property_copyAttributeList
获取一个 property 所有的属性(objc_property_attribute_t 结构体),通过 objc_property_attribute_t 可以获取到 getter , setter, 修饰符。
获取协议
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;
通过对<>
部分解析,我们可以得到属性的协议列表
关于属性的类型编码可以参考官方文档https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW5
YYClassInfo
YYClassInfo 是对一个 Class 的高层封装,数据结构如
@property (nonatomic, assign, readonly) Class cls; ///< class object
@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object
@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object
@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class
@property (nonatomic, strong, readonly) NSString *name; ///< class name
@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info
@property (nullable, nonatomic, strong, readonly) NSDictionary *ivarInfos; ///< ivars
@property (nullable, nonatomic, strong, readonly) NSDictionary *methodInfos; ///< methods
@property (nullable, nonatomic, strong, readonly) NSDictionary *propertyInfos; ///< properties
在内部包括了当 父类,元类,YYClassIvarInfo , YYClassMethodInfo, YYClassPropertyInfo 等相关信息。
建立索引
_update
为内部更新属性的方法,此方法重新生成 Class 的 ivarInfos
,methodInfos
, propertyInfos
信息。
建立ivarInfos
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);
}
建立methodInfos
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);
}
建立propertyInfos
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);
}
更新机制
当向一个类动态添加方法(class_addMethod()
),需要更新一个类的信息。调用setNeedUpdate
方法会把 _needUpdate
设置为 YES, 在下一次获取一个 class 的 YYClassInfo 的时候会根据这个字段来调用_update
来重新建立索引。
缓存
对一个类建立实例变量,属性,方法的索引之后,会对这个类进行缓存,以便下一次使用的时候不用重新建立索引,YYModel使用了 CFDictionaryCreateMutable 来进行缓存类的 info ,key 是类名,value 是 YYClassInfo 对象。使用 dispatch_semaphore_t 保证线程安全,之所以选择个是基于性能的考虑。
+ (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;
}
对映射处理的封装
_YYModelPropertyMeta
_YYModelPropertyMeta
是一个内部类,用来处理一个 Class 的 Property 的元数据类。和 YYClassPropertyInfo 不同的是,_YYModelPropertyMeta
还负责映射属性的维护。可以说是,对 YYClassPropertyInfo 更高层的封装,服务于映射的功能。
数据结构
NSString *_name; ///< property's name
YYEncodingType _type; ///< property's type
YYEncodingNSType _nsType; ///< property's Foundation type
BOOL _isCNumber; ///< is c number type
Class _cls; ///< property's class, or nil
Class _genericCls; ///< container's generic class, or nil if threr's no generic class
SEL _getter; ///< getter, or nil if the instances cannot respond
SEL _setter; ///< setter, or nil if the instances cannot respond
BOOL _isKVCCompatible; ///< YES if it can access with key-value coding
BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver
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
NSArray *_mappedToKeyPath; ///< the key path mapped to (nil if the name is not key path)
NSArray *_mappedToKeyArray; ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)
YYClassPropertyInfo *_info; ///< property's info
_YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.
维护几个关于映射的属性
- _name:属性的名字
- _type:属性的类型
- _nsType:属性的 NS类型,如 NSString,NSArray等系统类型
- _isCNumber:时候是 C 语言的数值类型,如 int, double
- _cls:属性所在的类
- _genericCls:此属性指出当属性是容器时候的类型,如 NSArray
- _getter和_setter: getter和setter方法
- _isKVCCompatible:指出是否可以支持KVC
- _isStructAvailableForKeyedArchiver:时候支持归档(服务于 YYModel 的 encode宏)
- _hasCustomClassFromDictionary:是否支持对莫个字典使用特定模型。
下面几个是关于一个属性在 JSON 中映射的key的描述
- _mappedToKey:在 JSON 的 key
- _mappedToKeyPath:以 KeyPath 描述的key(
@"desc" : @"ext.desc"
) - _mappedToKeyArray:映射多个 key 的信息存储(
@"bookID" : @[@"id",@"ID",@"book_id"]
) - _info:YYClassPropertyInfo 对象,上文中介绍过
- _next:如果多个属性映射到一个 key 的时候通过这个来查找。
初始化 _YYModelPropertyMeta
初始化_YYModelPropertyMeta
通过静态方法+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic
传入 propertyInfo 和 classInfo来完成初始化
流程大概如下
1.如果是属性是对象,通过 YYClassGetNSType 来从propertyInfo获取 NSType
- 判断属性是否可以归档。除了 CGSize ,CGPoint,CGRect,CGAffineTransform,UIEdgeInsets,UIOffset 其他都不支持归档
- 是否需要对字典设置单独的 class 通过类方法 modelCustomClassForDictionary 来返回特定的 model 类
- 判断是否支持 setter,和getter 方法。
- 判断时候支持 KVC。只有 long double 不支持KVC
_YYModelMeta
_YYModelMeta 是对一个需要映射的 Model 的封装。其数据结构为
YYClassInfo *_classInfo;
//这个是存储一个类最终的映射关系
/// Key:mapped key and key path, Value:_YYModelPropertyMeta.
NSDictionary *_mapper;
/// Array<_YYModelPropertyMeta>, all property meta of this model.
//所有需要处理的属性元数据
NSArray *_allPropertyMetas;
//嵌套类型的keypath的属性元数据
/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
NSArray *_keyPathPropertyMetas;
/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
//多key映射的的数组
NSArray *_multiKeysPropertyMetas;
/// 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;
- _mapper:维护了所有的映射关系,key 是属性名,value 是 _YYModelPropertyMeta 对象。
- _allPropertyMetas: 是所有映射属性 meta 的集合。
- _keyPathPropertyMetas: 所有使用 keyPath 形式的属性集合
- _multiKeysPropertyMetas: 所有多 key 映射的集合
- _keyMappedCount: 映射的 key 的统计
- _nsType: NS类型,如果是的话。
初始化过程
要生成一个 _YYModelMeta 需要 YYClassInfo 对象。
流程是:
- 获取黑名单和白名单。
- 创建 allPropertyMetas ,将所有属性添加到其中,根据黑,白名单剔除其中的元素。
- 将 allPropertyMetas 中的处理 keyPath 的 key 添加到 _keyPathPropertyMetas,将需要处理多 key 映射的添加到 _multiKeysPropertyMetas 中。
缓存机制
对于处理好的映射关系,YYModel 会对它们进行缓存,用的也是 CFDictionaryGetValue 来完成,使用 dispatch_semaphore_t 来保证线程安全
功能实现
JSON 转 Model
YYModel 使用方式简单,将转换方法写在了 NSObject 中的分类中,yy_modelWithJSON
是转换的入口,yy_modelWithJSON
中首先将 id 对象转换成字典,然后调用了yy_modelWithDictionary
,
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
Class cls = [self class];
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
NSObject *one = [cls new];
if ([one yy_modelSetWithDictionary:dictionary]) return one;
return nil;
首先获取当前 class ,之后 new 出相应的对象,使用类方法yy_modelSetWithDictionary
来将字典映射成 Model
- (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)];
if (modelMeta->_keyMappedCount == 0) return NO;
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id)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);
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
//需要对keypath和多key做处理
//对字典的key进行一一映射到model
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
//处理嵌套的映射
if (modelMeta->_keyPathPropertyMetas) {
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 {
//安全的,可以对所有属性进行映射。
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
//
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id)self) modelCustomTransformFromDictionary:dic];
}
return YES;
首先初始化 _YYModelMeta ,然后创建一个 ModelSetContext 结构体
typedef struct {
void *modelMeta; ///< _YYModelMeta
void *model; ///< id (self)
void *dictionary; ///< NSDictionary (json)
} ModelSetContext;
使用 CFDictionaryApplyFunction 对字典和属性调用 ModelSetWithDictionaryFunction 进行一一映射,对于有 keyPath 和多个 key 映射的情况使用 CFArrayApplyFunction 调用 ModelSetWithPropertyMetaArrayFunction
来完成多值映射的功能
数据校验
完成映射之后调用 modelCustomTransformFromDictionary 来判断 dic 是否完成,如果是返回 NO,则这个流程失败,返回nil,如果返回 YES, 则代表成功,返回此对象。
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id)self) modelCustomTransformFromDictionary:dic];
}
return YES;
NSObject *one = [cls new];
if ([one yy_modelSetWithDictionary:dictionary]) return one;
return nil;
Model 转 JSON
内部调用 ModelToJSONObjectRecursive
来完成,具体可以看链接。
Coding
Coding 调用的是 yy_modelEncodeWithCoder 主要思路是通过 Model 的 _YYModelMeta 获取其中的所有的属性集合 _allPropertyMetas ,循环遍历属性,如果是 C Number转换成 NSNumber ,如果是对象,直接 encode ,如果是 SEL 则转换成 NSString ,对于不能处理的类型直接抛出异常。
Copying
copying 调用 yy_modelCopy,实现方式先获取 _YYModelMeta,和 _allPropertyMetas。创建一个新对象,遍历 _allPropertyMetas 一一把值设置到新对象。
hash
遍历所有的属性,如果属性存在,把所有属性的值按位异或,所得的value就是hash。
如果此类没有属性,那么以这个对象的指针作为hash
equal
先判断2个对象的 hash,如果相同,在判断所有值是不是相同,如果相同则equal为YES,其他则为 NO
description
调用 ModelDescription 来完成功能.
YYModel 函数表
- YYClassGetNSType:通过一个 class 获取其 NSType,如果不是NSType,返回 YYEncodingTypeNSUnknown
- YYEncodingTypeIsCNumber:判断 YYEncodingType 是不是 c 语言数字类型,如 int,double
- YYNSNumberCreateFromID:从一个对象创建并且返回 NSNumber,如果失败则返回 nil,此函数对一下字符串做了特殊处理
dic = @{@"TRUE" : @(YES),
@"True" : @(YES),
@"true" : @(YES),
@"FALSE" : @(NO),
@"False" : @(NO),
@"false" : @(NO),
@"YES" : @(YES),
@"Yes" : @(YES),
@"yes" : @(YES),
@"NO" : @(NO),
@"No" : @(NO),
@"no" : @(NO),
@"NIL" : (id)kCFNull,
@"Nil" : (id)kCFNull,
@"nil" : (id)kCFNull,
@"NULL" : (id)kCFNull,
@"Null" : (id)kCFNull,
@"null" : (id)kCFNull,
@"(NULL)" : (id)kCFNull,
@"(Null)" : (id)kCFNull,
@"(null)" : (id)kCFNull,
@"" : (id)kCFNull,
@"" : (id)kCFNull,
@"" : (id)kCFNull};
YYNSDateFromString:从给定的一个时间字符串中获取 NSDate 对象。核心实现是通过判断时间字符串的长度,来判断时间的类型,如 Google , Github ,Facebook等的时间格式。
YYNSBlockClass:获取 NSBlock 的类型
static force_inline Class YYNSBlockClass() {
static Class cls;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
void (^block)(void) = ^{};
cls = ((NSObject *)block).class;
while (class_getSuperclass(cls) != [NSObject class]) {
cls = class_getSuperclass(cls);
}
});
return cls; // current is "NSBlock"
}
- YYISODateFormatter:获取 ISO 格式的 DateFormatter。
- YYValueForKeyPath:对一个字典使用 keyPath 来取值。如 a.b 的形式。
- YYValueForMultiKeys:从给定的多个 key 中获取字典中的 value,如果是 key 是数组,那么会调用 YYValueForKeyPath 方法
- ModelCreateNumberFromProperty:从给定的
_YYModelPropertyMeta
对象获取 NSNumber,如果失败,返回 nil 此方法使用 msg_send 来调用 getter 方法。 - ModelSetNumberToProperty:对给定的
_YYModelPropertyMeta
对象设置 NSNumber,此方法使用 msg_send 来调用 getter 方法。 - ModelSetValueForProperty:对给定的
_YYModelPropertyMeta
设置一个 value,如果是数字类型,则调用 ModelSetNumberToProperty,如果属性的 type 和设置 value 的 type 不一样,则会尝试转换。调用 msg_send 来设置 setter 方法。 - ModelSetWithDictionaryFunction:传入 key 和 value 之后调用 ModelSetValueForProperty 来对一个 key 来设置 value。
- ModelSetWithPropertyMetaArrayFunction:对于一个给定的
_propertyMeta
对象获取其的 mapper key,对于 keyPath 的调用 YYValueForKeyPath 来获取字典中的 value ,对多个 key 映射使用 YYValueForMultiKeys 来获取 value 之后调用 ModelSetValueForProperty 来设置属性的 value - ModelToJSONObjectRecursive:对于给定的 model 把自己的属性输出成 JSON 对象,如果是会自定义对象则递归的调用自己来输出。
- ModelDescriptionAddIndent:对于输出的字符串的缩进处理
- ModelDescription:此方法实现 Description 宏,会调用 ModelToJSONObjectRecursive 来将对象转换成 NSString,之后在用 ModelDescriptionAddIndent 来处理缩进