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>后,可获取YYModelVersionNumberYYModelVersionString的值

YYClassInfo解析

YYClassInfo把runtime中的objc_varobjc_methodobjc_classproperty_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中的nametypeEncoding都是使用strong修饰的,因为用strong单纯地做一次强引用在性能上是优于copy的

NSObject+YYModel解析

NSObject+YYModelYYClassInfo封装好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

%s 个评论

要回复文章请先登录注册