Mantle 源码解读

2017-08-22  本文已影响61人  Sev3nUp

Mantle 的类层次关系图:

Mantle 的类层次关系图

MTLJSONSerializing 该协议可以让用户自定义哪些JSON字段,映射成 CustomModel 的哪些属性,名称不一致的时候,极为有用。

可以看到 Mantle 核心的内容就是两个类,我们主要分析这两个类的一些核心方法。

先分析一下MTLModel 类,从最上层的函数开始,一步一步深入分析:

MTLModel init方法

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 方法生成。

generateAndCacheStorageBehaviors

这个方法,分四步:

  1. 遍历所有属性。

  2. 根据属性的存储类型,进行分类。

  3. 分好类,分别添加 至对应的集合,并且设置关联对象,使用copy 模式。

  4. 循环遍历属性,调用 block 进行设置。

第1步遍历所有属性的方法为:propertyKeys 判断其拷贝行为,过滤掉不做存储的属性,需要注意的是它同时会对 hash 、 superclass 、 description 、 debugDescription 这四个属性进行判断,在 NSObject 内这四个都是 readonly 的,如果你不去将它设为 readwrite 的话,它们是不做存储的。

这里再讲一下Mantle的存储行为。

MTLModel用了一个枚举 MTLPropertyStorage 来标记一个属性的拷贝行为,分为三类:

  1. MTLPropertyStorageNone :属性不做任何存储,在 MTLModel里判断不存储的条件是1.没有该属性,自然不用存储 2.该属性没有使用 @dynamic 指令,但是没有成员变量,并且没有对应的setter和getter方法 3. MTLModel 类中属性是只读,且没有成员变量。

  2. MTLPropertyStorageTransitory :属性只做暂时性的存储,在官方解释里看到一句话 It may disappear at any time ,感觉指的是弱引用的属性,但是在 MTLModel 里并没有看到返回 MTLPropertyStorageTransitory 的处理。

  3. MTLPropertyStoragePermanent ,属性做永久存储, MTLModel 里判断只要不是 MTLPropertyStorageNone 就是 MTLPropertyStoragePermanent,需要做暂时存储的,就需要在子类里重写了。

遍历所有属性方法

第4步循环遍历所有属性(包括本类及其父类)的方法为:

循环遍历属性方法

这里遍历类及其父类,一直到 MTLModel 根类为止。获取它的属性,如果没有属性值继续父类,如果有,使用 onExit 语法(相当于 Swift 上 defer 关键字),在作用域结束的时候,释放掉 properties。

onExit 定义:

onExit 定义

attribute((cleanup(mtl_executeCleanupBlock)) 在作用域离开的时候,进行清理工作。会调用mtl_executeCleanupBlock函数,并将 block 的地址传入,在函数体内直接调用这个 block。

验证属性的存储类型方法:

验证属性的存储类型方法

这里面用到一个属性结构,方便管理一个属性的描述内容。如果一个属性没有getter, setter, 并且没有对应的变量,则返回.none 类型。如果是只读且没有对应的变量,到了根类 MTLModel 返回 none,否则验证父类的关于这个键值的存储类型。其他返回的都是 permanent 类型。

到目前为止,MTLModel 将 model转 dictionary 就完成了。

接下来分析一下 MTLJSONAdapter 类和 MTLJSONSerializing 协议。

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 数据,调用第一个方法。

  1. 从 model class 上获取所有的属性,参考上面的MTLModel propertyKeys方法;

  2. 遍历这些属性,并且根据属性作为 key 获取 JSON 上的 value;如果没有值,不用转换,直接 continue,继续下一个循环;

  3. 获取属性的转换器 valueTransformer,用户从 JSON value 转换到自定义的值,比如 long 类型的时间戳,转换到 NSDate 类型,也可以从 JSON的字符串,映射到 Objective-C 枚举类型;

  4. 将 key 和转换后的 value,构建一个键值对,保存起来,最终形成一个 dicionaryValue;

  5. 使用 MTLModel 的Dictionary 转换成 Model 对象的方法;

  6. 使用 KVC 验证转换后的 model 是否有效,无效返回 nil,有效则返回 model 对象。

通过以上六步,将 JSON data,通过 Model class,转换成 model object。

2. model object -> using Model class -> JSON data

第一个方法的核心内容是 JSONDictionaryFromModel: error: 方法,数组类型的,需要循环遍历数组中 model 对象,将其出入第一个方法。

  1. 核对 model class 类型;

  2. 获取需要转换的 keys。 model 实现的 MTLJSONSerializing 协议中的 JSONKeyPathsByPropertyKey 方法,并将这些 keys 对应 model class 的 values,构建 dictionaryValue;

  3. 遍历dictionaryValue, 获取 JSON 上的 keyPaths;

  4. 获取 model 定义的 value transformer;

  5. 是否允许反向转换;

  6. 如果定义这个 reverseTransformedValue: success: error: 方法,转换错误处理,更加安全;

  7. 如果没有,调用 reverseTransformedValue 换成 value。如果返回 nil,则设置为 NSNull.null;

  8. 创建一个根据 keyPath,分割键值,填充对象的 createComponents block;

  9. 如果 JSONKeyPaths是 字符串类型,则将转换后的 value 对象,key 为 JSONKeyPaths, 构建键值对,并保存起来;

  10. 如果 JSONKeyPaths 是数组,遍历这个路径, 对于每个 value, 调用第八步的 createComponents block,并且执行第九步的操作。

经过以上十步,完成了model object 通过 model class,转换成 JSON dictionary data,可以将其发送给服务器端。

以上,就是 Mantle 实现的核心内容。

上一篇下一篇

猜你喜欢

热点阅读