YYModel - 源码解析
概要
YYModel是一个高性能 iOS/OSX 模型转换框架,由头文件YYModel.h、自定义类YYClassInfo和NSObject的YYModel分类组成,共5个文件
- 高性能: 模型转换性能接近手写解析代码
- 自动类型转换: 对象类型可以自动转换,详情见下方表格
- 类型安全: 转换过程中,所有的数据类型都会被检测一遍,以保证类型安全,避免崩溃问题
- 无侵入性: 模型无需继承自其他基类
- 轻量: 该框架只有5个文件 (包括.h文件)
- 文档和单元测试: 文档覆盖率100%, 代码覆盖率99.6%
YYModel.h解析
YYModel.h设置了头文件的引入方式,代码仅22行
#import <Foundation/Foundation.h>
#if __has_include(<YYModel/YYModel.h>)
FOUNDATION_EXPORT double YYModelVersionNumber;
FOUNDATION_EXPORT const unsigned char YYModelVersionString[];
#import <YYModel/NSObject+YYModel.h>
#import <YYModel/YYClassInfo.h>
#else
#import "NSObject+YYModel.h"
#import "YYClassInfo.h"
#endif
__has_include
是编译器的宏,传入一个引入文件的名称,如果该文件能够被引入则返回1,否则返回0
FOUNDATION_EXPORT
是和#define
类似的定义常量的方法,#define
定义的字符串常量需要用isEqualToString:
比较,而FOUNDATION_EXPORT
比较的是地址,效率更高
外部工程在#import <YYModel/YYModel.h>
后,可获取YYModelVersionNumber
和YYModelVersionString
的值
YYClassInfo解析
YYClassInfo
把runtime中的objc_var
、objc_method
、objc_class
和property_t
结构体做了一层封装,然后根据Type Encodings的定义封装了自己的YYEncodingType
,ObjC中所有的参数类型都被纳入其中
本文基于runtime源码818.2版本做分析
以objc_ivar
为例,对应封装的类为YYClassIvarInfo
/**
Instance variable information.
*/
@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
而runtime.h中对于objc_ivar
的定义是这样的
struct objc_ivar {
char * _Nullable ivar_name OBJC2_UNAVAILABLE;
char * _Nullable ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
需要注意的是,YYClassIvarInfo
中的name
和typeEncoding
都是使用strong修饰的,因为用strong单纯地做一次强引用在性能上是优于copy的
NSObject+YYModel解析
NSObject+YYModel
在YYClassInfo
封装好runtime结构体的基础上,以无侵入的方式实现类和JSON之间的转换
NSObject+YYModel
新构建了两个以下划线开头的私有类,_YYModelPropertyMeta
封装属性信息,里面包含了YYClassPropertyInfo
,_YYModelMeta
封装类信息,里面包含了YYClassInfo
NSObject+YYModel
定义了ObjC对象类型的枚举类型YYEncodingNSType
,并通过isSubclassOfClass:
方法做判断
/// Foundation Class Type
typedef NS_ENUM (NSUInteger, YYEncodingNSType) {
YYEncodingTypeNSUnknown = 0,
YYEncodingTypeNSString,
YYEncodingTypeNSMutableString,
YYEncodingTypeNSValue,
YYEncodingTypeNSNumber,
YYEncodingTypeNSDecimalNumber,
YYEncodingTypeNSData,
YYEncodingTypeNSMutableData,
YYEncodingTypeNSDate,
YYEncodingTypeNSURL,
YYEncodingTypeNSArray,
YYEncodingTypeNSMutableArray,
YYEncodingTypeNSDictionary,
YYEncodingTypeNSMutableDictionary,
YYEncodingTypeNSSet,
YYEncodingTypeNSMutableSet,
};
/// Get the Foundation class type from property info.
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;
}
NSObject+YYModel
还定义了表示内联的宏force_inline
,广泛使用在内部定义的诸多静态方法中
#define force_inline __inline__ __attribute__((always_inline))
NSObject+YYModel
实现类和JSON之间的转换,一共有1800多行代码,具体实现细节暂不讨论,涉及了众多runtime方法的调用,需要对runtime有一定程度的认识