阅读 109

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
链接:https://juejin.cn/post/7030434312604352525


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐