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有一定程度的认识
作者:FakeCoder
链接:https://juejin.cn/post/7030434312604352525