前言
YYModel :一个非常轻量级的 JSON 模型自动转换库,代码风格良好且思路清晰,可以从源码中看到作者对 Runtime 深厚的理解。难能可贵的是 YYModel 在其轻量级的代码下还保留着自动类型转换,类型安全,无侵入等特性,并且具有接近手写解析代码的超高性能。
一、先从objc4说起
Class
1 | struct objc_class { |
Method(方法)、Ivar(成员变量)、Property(属性)
1 | typedef struct method_t *Method; |
Method(方法)
1 | struct objc_method { |
1 | struct objc_method_list { |
1 | struct objc_method_description { |
1 | struct objc_method_description_list { |
1 | //获取方法列表 |
Ivar(成员变量)
1 | struct objc_ivar_list { |
1 | struct objc_ivar { |
1 | //基地址偏移字节, 对象地址 + ivar偏移字节 -> 访问ivar |
Property(属性)
1 | struct property_t { |
1 | typedef struct { |
1 | //获取Class的属性列表 |
1 | //复制单个属性的详细: |
举个例子:
1 | @property (nonatomic, copy, getter=getName, setter=setName:) NSString *name; |
获取name的Attributes<name,value>:
index | name | value | 描述 |
---|---|---|---|
0 | T | @\”NSString\” | 类型 |
1 | C | - | copy |
2 | N | - | nonatomic |
3 | G | getName | getter |
4 | S | setName: | setter |
5 | V | _name | ivarName |
还有很多其他苹果规定的type,具体可查看以下链接:
Type Encodings
Declared Properties
Others
1 | struct objc_category { |
1 | struct objc_protocol_list { |
1 | struct objc_cache { |
–
二、YYModel文件目录
3个文件:
1 | YYModel.h |
YYModel.h
1 | YYModel.h |
YYClassInfo:
管理 4个类,分别是对class、ivar、method、property的封装
1 | YYClassInfo.h |
将 Runtime 层级的一些结构体封装到 NSObject 层级以便调用;
YYClassInfo(NSObject层级) | Runtime层级 |
---|---|
YYClassInfo | objc_class |
YYClassIvarInfo | objc_ivar |
YYClassMethodInfo | objc_method |
YYClassPropertyInfo | property_t |
NSObject+YYModel:
主要负责提供方便调用的接口、实现具体的模型转换逻辑(借助YYClassInfo 中的封装)
- 2个私有类(_YYModelMeta、_YYModelPropertyMeta)
- 1个主类(NSObject (YYModel))
- 1个协议(@protocol YYModel
)
1 | NSObject+YYModel.h |
- _YYModelMeta :表示Model的类信息,包含YYClassInfo;
- _YYModelPropertyMeta :表示Model对象的属性信息,包含YYClassPropertyInfo;
以下三者关系:
- _YYModelMeta、_YYModelPropertyMeta
- YYClassInfo(NSObject层级)
- Runtime层级
NSObject+YYModel(逻辑实现层) | YYClassInfo(NSObject层级) | Runtime层级 |
---|---|---|
_YYModelMeta | YYClassInfo | objc_class |
- | YYClassIvarInfo | objc_ivar |
- | YYClassMethodInfo | objc_method |
_YYModelPropertyMeta | YYClassPropertyInfo | property_t |
三、模型转换逻辑实现
1.JSON转Model分三个步骤:
- JSON - to - Data
- Data - to - Dictionary
- Dictionary - to - Model
1 | //JSON —> Data |
1 | //Data —> Dictionary |
1 | //dictionary —> Model (一个方法就实现了!) |
2.具体分析 dictionary 转换 Model
主要实现方法:
- (BOOL)modelSetWithDictionary:(NSDictionary *)dic;
看这个方法之前,先看看_YYModelMeta、_YYModelPropertyMeta的包:
_YYModelMeta:
1 | @interface _YYModelMeta : NSObject { |
_YYModelPropertyMeta
1 | @interface _YYModelPropertyMeta : NSObject { |
再看两个函数:CFArrayApplyFunction() 和 CFDictionaryApplyFunction()
CF(CoreFoundation)相对NS(Foundation)遍历容器能提升性能
1 | //遍历字典,将参数context传给applier;需要重写applier方法,接收context; |
1 | //遍历数组,将参数context传给applier;需要重写applier方法,接收context; |
主要实现方法:
- (BOOL)modelSetWithDictionary:(NSDictionary *)dic;
1 | //dictionary 转换 Model主要实现方法 |
上述方法主要干了这么几件事:
- 验证参数(为了程序健壮性一定要这么做)
- 创建YYModelMeta对象
- 创建ModelSetContext结构体
- 将字典用ModelSetWithDictionaryFunction解析(遍历字典去setModel)
- 根据字典中存在keyPath、keys、key对应的value是数组的情况,都调用ModelSetWithPropertyMetaArrayFunction解析(遍历数组去setModel)
遍历数组
ModelSetWithPropertyMetaArrayFunction
1 |
|
遍历字典
ModelSetWithDictionaryFunction
1 |
|
最终的解析方法:遍历字典或数组中每一项属性
1 |
|
为了看清实现逻辑,精简代码如下:
1 |
|
Number类型转成属性
1 |
|
最终通过objc_msgSend发送消息的形式,实现model的该属性的setter方法
1 | ((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)model, meta->_setter, num.boolValue); |
1 | /** objc_msgSend |
1 | //实现的伪代码... |
以上大概就是json-to-model整个过程
三、model-to-json过程解析
最后解析的json要满足苹果规定:
- 最后解析的json一定是字典或数组
- json内每一项都应该是NSString、NSNumber、NSArray、NSDictionary、NSNull的一种
- 所有字典的key都应该是NSString
- 值类型不能是非数值的或无穷的
- (id)modelToJSONObject;
1 | - (id)modelToJSONObject { |
static id ModelToJSONObjectRecursive(NSObject *model);
1 | static id ModelToJSONObjectRecursive(NSObject *model) { |
以上大概就是model-to-json的过程
另外,还有model-to-data、model-to-string等方法,都是先调用model-to-json,然后再利用系统的NSJSONSerialization、initWithData:encoding:进行转换
####### modelToJSONData1
2
3
4
5
6//先model-to-json,再json-to-data
- (NSData *)modelToJSONData {
id jsonObject = [self modelToJSONObject];
if (!jsonObject) return nil;
return [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:NULL];
}
####### modelToJSONString1
2
3
4
5
6//先model-to-data,再data-to-string
- (NSString *)modelToJSONString {
NSData *jsonData = [self modelToJSONData];
if (jsonData.length == 0) return nil;
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
以上,解析json-to-model、model-to-json的转换过程解读完毕
四、一些宏的使用
1、NS_ASSUME_NONNULL_BEGIN、NS_ASSUME_NONNULL_END
我们可以给每个属性或每个方法都去指定 nullable 和 _nonnull,当一个类中,有的方法或属性给它指定了 nullable 和 _nonnull,其他方法又没有指定,编译器会爆警告。这时候可以全局使用NS_ASSUME_NONNULL_BEGIN、NS_ASSUME_NONNULL_END,然后再单独指定你要指定为__nullable的属性和方法。
2、#define force_inline inline attribute((always_inline))
attribute((always_inline))的意思是强制内联,
所有加了attribute((always_inline))的函数再被调用时不会被编译成函数调用而是直接扩展到调用函数体内
1 | force_inline void a() { |
b调用a函数的汇编代码不会是跳转到a执行,而是a函数的代码直接在b内成为b的一部分。
用force_inline代替 inline attribute((always_inline)),是因为比较方便。