美文网首页
Mantle 源码解读

Mantle 源码解读

作者: Sev3nUp | 来源:发表于2017-08-22 16:29 被阅读61次

    Mantle 的类层次关系图:

    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 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 实现的核心内容。

    相关文章

      网友评论

          本文标题:Mantle 源码解读

          本文链接:https://www.haomeiwen.com/subject/kosldxtx.html