Mantle 的类层次关系图:
CustomModel 类,就是我们自定义的 Model,在 MVC 或者 MVVM 开发模式上必不可少的 Model 类,如:User, Note, Tage 等。
-
基类MTLModel 提供了一些默认的行为来处理对象的初始化和归档操作,同时可以获取到对象所有属性的键值集合。主要是两件事:
- 将dictionary 键值对,映射成该类的所有属性(名字为 key)的值;
- 将类的所有属性作为 key,属性值作为 value,转换成dictionary;
MTLJSONSerializing 该协议可以让用户自定义哪些JSON字段,映射成 CustomModel 的哪些属性,名称不一致的时候,极为有用。
- MTLJSONAdaptor 起到一个适配器的作用,将 JSON Dict 进行验证,将其键和值,通过 CustomModel 上的属性transformer,将 JSON的 value 转换成定义的需要的类型 value, 比如将时间戳 long 类型,转换成 NSDate 类型,构建一个 Dictionary,传入给 MTLModel构造方法,完成 JSON 到 model 的转换;同理,通过 model 类型的 dictionary, 通过 model 反向 transformer 将 model 属性值,转换成 JSON value后,构建一个 JSON Dict。这个 JSON Dict 就是我们需要通过网络发送给服务器的结构。
可以看到 Mantle 核心的内容就是两个类,我们主要分析这两个类的一些核心方法。
先分析一下MTLModel 类,从最上层的函数开始,一步一步深入分析:
MTLModel 通过这个初始化方法,从 dictionary,初始化一个 Model 对象。将 dictionary 上的 key 与 value,通过 KVC 机制,将 model 的属性进行初始化,遍历整个 dictionary 都没有发生错误时,则返回初始化对象。如果在 KVC 验证过程中发生错误,则直接返回 nil,终止循环。在 MTLValidateAndSetValue
方法的第四个参数,forceUpdate,传入 YES,表示验证通过后,强制设置值,方法体的内容如下:
如此就完成了 Dictionary 转换成 Model 类。
MTLModel 定义了一个 dictionaryValue 属性,并定义了 getter 方法。目的是完成 model 转换为 dictionary:
将 transitory 属性键与 permanent 属性键相加,然后通过 KVC 获取这些键的值,组成一个 dictionary 作为返回值。
从关联对象上获取这些,如果为 nil,调用 generateAndCacheStorageBehaviors
方法生成。
这个方法,分四步:
遍历所有属性。
根据属性的存储类型,进行分类。
分好类,分别添加 至对应的集合,并且设置关联对象,使用copy 模式。
循环遍历属性,调用 block 进行设置。
第1步遍历所有属性的方法为:propertyKeys
判断其拷贝行为,过滤掉不做存储的属性,需要注意的是它同时会对 hash 、 superclass 、 description 、 debugDescription 这四个属性进行判断,在 NSObject 内这四个都是 readonly 的,如果你不去将它设为 readwrite 的话,它们是不做存储的。
这里再讲一下Mantle的存储行为。
MTLModel用了一个枚举 MTLPropertyStorage 来标记一个属性的拷贝行为,分为三类:
MTLPropertyStorageNone :属性不做任何存储,在 MTLModel里判断不存储的条件是1.没有该属性,自然不用存储 2.该属性没有使用 @dynamic 指令,但是没有成员变量,并且没有对应的setter和getter方法 3. MTLModel 类中属性是只读,且没有成员变量。
MTLPropertyStorageTransitory :属性只做暂时性的存储,在官方解释里看到一句话
It may disappear at any time
,感觉指的是弱引用的属性,但是在 MTLModel 里并没有看到返回MTLPropertyStorageTransitory
的处理。MTLPropertyStoragePermanent ,属性做永久存储, MTLModel 里判断只要不是 MTLPropertyStorageNone 就是 MTLPropertyStoragePermanent,需要做暂时存储的,就需要在子类里重写了。
第4步循环遍历所有属性(包括本类及其父类)的方法为:
这里遍历类及其父类,一直到 MTLModel 根类为止。获取它的属性,如果没有属性值继续父类,如果有,使用 onExit 语法(相当于 Swift 上 defer 关键字),在作用域结束的时候,释放掉 properties。
onExit 定义:
attribute((cleanup(mtl_executeCleanupBlock)) 在作用域离开的时候,进行清理工作。会调用mtl_executeCleanupBlock函数,并将 block 的地址传入,在函数体内直接调用这个 block。
验证属性的存储类型方法:
这里面用到一个属性结构,方便管理一个属性的描述内容。如果一个属性没有getter, setter, 并且没有对应的变量,则返回.none 类型。如果是只读且没有对应的变量,到了根类 MTLModel 返回 none,否则验证父类的关于这个键值的存储类型。其他返回的都是 permanent 类型。
到目前为止,MTLModel 将 model转 dictionary 就完成了。
接下来分析一下 MTLJSONAdapter 类和 MTLJSONSerializing 协议。
MTLJSONSerializing:
第一个方法,是必须实现的方法,它是用于将属性和 JSON 的解析路径做关联,它不仅仅可以用于给属性起“别名”,还可以用于多级解析和多层嵌套,用官方例子来举例:
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return @{
@"name": @"POI.name",
@"point": @[ @"latitude", @"longitude" ],
@"starred": @"starred"
};
}
在映射过程中 starred 与 JSONDictionary[@"starred"] 做映射,
name 与 JSONDictionary[@"POI"][@"name"] 做映射,
point 则等同于以下这个 dictionary
@{
@"latitude": JSONDictionary[@"latitude"],
@"longitude": JSONDictionary[@"longitude"]
}
第二个方法,是 model 实现的 +< key >JSONTransformer
方法,如果有则使用它进行转换的值。
第三个方法是用于类簇。
@interface XYMessage : MTLModel
@end
@interface XYTextMessage: XYMessage
@property (readonly, nonatomic, copy) NSString *body;
@end
@interface XYPictureMessage : XYMessage
@property (readonly, nonatomic, strong) NSURL *imageURL;
@end
@implementation XYMessage
+ (Class)classForParsingJSONDictionary:(NSDictionary *)JSONDictionary {
if (JSONDictionary[@"image_url"] != nil) {
return XYPictureMessage.class;
}
if (JSONDictionary[@"body"] != nil) {
return XYTextMessage.class;
}
NSAssert(NO, @"No matching class for the JSON dictionary '%@'.", JSONDictionary);
return self;
}
@end
MTLJSONAdapter
1. JSON data -> using Model class -> model object
第一个方法的核心内容是 modelFromJSONDictionary: error:
方法。数组的类型,需要循环遍历数组内的 JSONDictionary 数据,调用第一个方法。
从 model class 上获取所有的属性,参考上面的MTLModel propertyKeys方法;
遍历这些属性,并且根据属性作为 key 获取 JSON 上的 value;如果没有值,不用转换,直接 continue,继续下一个循环;
获取属性的转换器 valueTransformer,用户从 JSON value 转换到自定义的值,比如 long 类型的时间戳,转换到 NSDate 类型,也可以从 JSON的字符串,映射到 Objective-C 枚举类型;
将 key 和转换后的 value,构建一个键值对,保存起来,最终形成一个 dicionaryValue;
使用 MTLModel 的Dictionary 转换成 Model 对象的方法;
使用 KVC 验证转换后的 model 是否有效,无效返回 nil,有效则返回 model 对象。
通过以上六步,将 JSON data,通过 Model class,转换成 model object。
2. model object -> using Model class -> JSON data
第一个方法的核心内容是 JSONDictionaryFromModel: error:
方法,数组类型的,需要循环遍历数组中 model 对象,将其出入第一个方法。
核对 model class 类型;
获取需要转换的 keys。 model 实现的 MTLJSONSerializing 协议中的
JSONKeyPathsByPropertyKey
方法,并将这些 keys 对应 model class 的 values,构建 dictionaryValue;遍历dictionaryValue, 获取 JSON 上的 keyPaths;
获取 model 定义的 value transformer;
是否允许反向转换;
如果定义这个
reverseTransformedValue: success: error:
方法,转换错误处理,更加安全;如果没有,调用
reverseTransformedValue
换成 value。如果返回 nil,则设置为 NSNull.null;创建一个根据 keyPath,分割键值,填充对象的 createComponents block;
如果 JSONKeyPaths是 字符串类型,则将转换后的 value 对象,key 为 JSONKeyPaths, 构建键值对,并保存起来;
如果 JSONKeyPaths 是数组,遍历这个路径, 对于每个 value, 调用第八步的 createComponents block,并且执行第九步的操作。
经过以上十步,完成了model object 通过 model class,转换成 JSON dictionary data,可以将其发送给服务器端。
以上,就是 Mantle 实现的核心内容。