从YYModel源码中可以学到什么:前篇
前言
YYModel
一个高性能模型框架。
十年的石狮网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。成都全网营销的优势是能够根据用户设备显示端的尺寸不同,自动调整石狮建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。创新互联从事“石狮网站设计”,“石狮网站推广”以来,每个客户项目都认真落实执行。
作者在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()
FOUNDATION_EXPORT double YYModelVersionNumber;
FOUNDATION_EXPORT const unsigned char YYModelVersionString[];
#import
#import
#else
#import "NSObject+YYModel.h"
#import "YYClassInfo.h"
#endif
拓展:
FOUNDATION_EXPORT
是用来定义常量的,另外一个经常用到的#define
定义常量。那么两者的区别?
假设分别使用两者定义字符串常量,前者可以通过==
来判断字符串是否相等,后者则需要使用isEqualToString:
来判断。因为,前者比较的是字符串指针地址,后者比较每个字符,因此前者效率更高。
__has_include()
#if __has_include(<UIKit/UIKit.h>) // 包含 #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分类
提供了一些data
与model
转换的方法。
// 根据接收到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 *)modelCustomPropertyMapper;
// 如果属性是一个容器对象,例如NSArray/NSSet/NSDictonary,实现该方法可以返回一个映射字典(property -> class)
+ (nullable NSDictionary *)modelContainerPropertyGenericClass;
+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
// 白名单和黑名单(若实现,忽略黑名单,只处理白名单)
+ (nullable NSArray *)modelPropertyBlacklist;
+ (nullable NSArray *)modelPropertyWhitelist;
- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
// 数据验证和自定义转换
// 当JSON转为Model完成后,会调用该方法。可以在该方法中进行校验工作,返回NO该Model被忽略,也可以完成一些转换工作。
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
拓展
在源码中会发现有一对
NS_ASSUME_NONNULL_BEGIN
和NS_ASSUME_NONNULL_END
宏。在Swift中存在
Option
类型,可以使用!
和?
声明变量,但是在OC中没有这个特性。出现新的关键词用于OC转Swift时区分能否为空。
nullable
&&nonnull
nullable
指对象可以为NULL。
nonnull
指对象不可以为NULL。如果不遵循这一规则,编译器就会给出警告。
为了简化书写,在
NS_ASSUME_NONNULL_BEGIN
和NS_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_千
分享文章:从YYModel源码中可以学到什么:前篇
当前链接:http://pwwzsj.com/article/jcesoj.html