前言

YYModel一个高性能模型框架。

作者在Github上给出的性能对比图(iphone 6 y:时间)

YYModel:具体以下特点:高性能、自动类型转换、类型安全、非侵入性、轻量等。

关于如何使用YYModel查看文档和示例【传送门】。

本文主要任务,分析YYModel的整体架构,实现思路,涉及到的知识点。

版本:1.0.4

文件结构

YYModel,只有5个文件。接下我们会具体看这五个文件都做了什么工作。

YYModel.h头文件,通过#import该文件使用库。YYClassInfo.h 根据名字应该能猜出,关于Class信息的文件。NSObject + YYModel.h 这个NSObject的一个Category。还定义了一些内部类。

YYModel头文件

该文件只是一个头文件,代码很少。

#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

拓展:

FOUNDATION_EXPORT是用来定义常量的,另外一个经常用到的#define定义常量。

那么两者的区别?
假设分别使用两者定义字符串常量,前者可以通过==来判断字符串是否相等,后者则需要使用isEqualToString:来判断。因为,前者比较的是字符串指针地址,后者比较每个字符,因此前者效率更高。

__has_include()

#if __has_include(&lt;UIKit/UIKit.h&gt;)// 包含#else// 不包含#endif

判断UIKit库是否存在。

YYClassInfo

YYClassInfo文件中定义四个类,涉及到Runtime知识,请看这篇文章博文或者直接查看[objc4源码]()

YYClassIvarInfo

该类对应实例变量信息(ivars),包含:名称,偏移量,类型编码,类型;其中类型请查看【官方文档Type Encodings】

YYClassMethodInfo

方法的信息类,包含,方法名称,SEL,IMP,参数类型编码,返回值类型编码等。

YYClassPropertyInfo

属性信息类,包含名称,类型,类型编码,ivar名称,类,协议列表,setter/getter等。

YYClassInfo:类信息。

拓展

Ivar, Method, Property, SEL, IMP都是什么?

理解这些需要对runtime了解,上面给出了博文链接,这里简单复习一下。

typedef struct objc_method *Method; // 方法typedef struct objc_ivar *Ivar; // 实例变量typedef struct objc_category *Category; //分类typedef struct objc_property *objc_property_t; // 属性struct objc_class { Class isa Class super_class // 指向父类 const char * name // 名称 long version long info long instance_size struct objc_ivar_list * ivars // 实例变量表 struct objc_method_list * * methodLists //方法表 struct objc_cache * cache struct objc_protocol_list * protocols //协议表 };/* Use `Class` instead of `struct objc_class *` */

Ivar指实例变量,存放在实例变量表中。Method方法,存放在方法表中。

接下再看一下Objc_method结构体

struct objc_method { SEL method_name char * method_types IMP method_imp }

SEL指方法名称,IMP指方法实现。

NSObject + YYModel

该文件定义三个分类和一个协议,以及两个内部类,下面是.h文件中提供的接口。

NSObject分类

提供了一些datamodel转换的方法。

// 根据接收到JSON创建一个实例,该方法是线程安全的。// json对象可以是 NSDictionary,NSString,NSData.+ (nullable instancetype)yy_modelWithJSON:(id)json;// 字典转Model + (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary;// 通过json设置属性, 无效的数据会被忽略- (BOOL)yy_modelSetWithJSON:(id)json;- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic;// model转json对象(NSDictionary/NSArray), NSData, NSString- (nullable id)yy_modelToJSONObject;- (nullable NSData *)yy_modelToJSONData;- (nullable NSString *)yy_modelToJSONString;#pragma mark - 其他快捷方法// 拷贝(NSCoping协议)- (nullable id)yy_modelCopy;// 编码和解码(对应NSCoding协议的两个方法)- (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder;- (id)yy_modelInitWithCoder:(NSCoder *)aDecoder;// NSObject协议// 哈希值- (NSUInteger)yy_modelHash;// 相等判断- (BOOL)yy_modelIsEqual:(id)model;// Debug描述- (NSString *)yy_modelDescription;

以上NSObject分类中提供的接口,具体实现稍后学习。

// 直接添加以下代码即可自动完成- (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; }- (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; }- (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; }- (NSUInteger)hash { return [self yy_modelHash]; }- (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; }- (NSString *)description { return [self yy_modelDescription]; }

NSArray分类

从json-array中创建一个数组, 其实也是遍历循环调用字典转Model。

+ (nullable NSArray *)yy_modelArrayWithClass:(Class)cls json:(id)json;NSDictionary分类

从json创建字典。

+ (nullable NSDictionary *)yy_modelDictionaryWithClass:(Class)cls json:(id)json;YYModel协议

YYModel协议,通过实现响应的方法,可以提供白名单,黑名单,自定义属性名称等功能。

// 自定义属性名称,可以将json中的名称映射到自定义的名称,可以解决冲突例如`id`。+ (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;// 如果属性是一个容器对象,例如NSArray/NSSet/NSDictonary,实现该方法可以返回一个映射字典(property -> class)+ (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;// 白名单和黑名单(若实现,忽略黑名单,只处理白名单)+ (nullable NSArray<NSString *> *)modelPropertyBlacklist;+ (nullable NSArray<NSString *> *)modelPropertyWhitelist;- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;// 数据验证和自定义转换// 当JSON转为Model完成后,会调用该方法。可以在该方法中进行校验工作,返回NO该Model被忽略,也可以完成一些转换工作。- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;

拓展

在源码中会发现有一对NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END宏。

在Swift中存在Option类型,可以使用!?声明变量,但是在OC中没有这个特性。出现新的关键词用于OC转Swift时区分能否为空。

nullable && nonnull

nullable指对象可以为NULL。

nonnull指对象不可以为NULL。

如果不遵循这一规则,编译器就会给出警告。

为了简化书写,在NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END宏之间的代码,默认都是nonnull。所以我们只需指定哪些nullable的指针就可以了。
StackOverflow关于该宏的问题。

其他知识点

接下来补充一些知识点,或许对以后开发有帮助。

NS_OPTIONS && NS_ENUM

这是两个简单方便的宏定义,从iOS6开始,他们取代了原来的enum

例如:

typedef NS_ENUM(NSInteger, UITableViewCellStyle) { UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, UITableViewCellStyleSubtitle};

其中第一个元素存储类型。第二个参数是名字。

另外,enum也可以被定义为按位掩码。用简单的OR和AND数学运算既可实现一个整型值的编码。请看这篇文章《NS_ OPTIONS && NS _ENUM - NShipster》。

小结

本章主要整理YYModel整体框架以及开发者提供的接口,并没有涉及到内部实现。接下来的文章,我将会一步步分析源码实现。

参考

Github - ibireme/YYModel

个人博客Owenli
微博Owenli_千