ObjectMapper 源码阅读笔记

ObjectMapper 是一款基于Swfit非常有用的Json转模型的第三方框架。 ObjectMapper 和原生的Codable 来转模型的区别在我的另一篇文章Swift 转模型 Codable和ObjectMapper 使用比较 中提过,这里就不再重复。

导读: 用ObjectMapper 转模型
book.json

{
    "title": "War and Peace: A protocol oriented approach to diplomacy",
    "author": "A. Keed Decoder",
    "rating": 5.0,
}

XXBook模型

import ObjectMapper

struct XXBook: Mappable {
    // 通过Mapper转模型
    var title: String?
    var author: String?
    var rating: Float?
    
    init?(map: Map) {
        
    }
    
    mutating func mapping(map: Map) {
        title            <- map["title"]
        author           <- map["author"]
        rating           <- map["rating"]
    }
}

使用: 将json转成功之后直接调用

// 用ObjectMapper转模型
let book = XXBook(JSON: json) 

下面来探索为什么 XXBook(JSON: json) 就可以转模型成功。这篇文章仅从Json转模型中的主要方法进行分析,会略过暂未用到的逻辑。

这里放一张协议方法图


ObjectMapper 源码阅读笔记_第1张图片
ObjectMapper协议.png

主流程

一、首先XXBook 遵循了Mappable协议, Mappable 又遵循了BaseMappable协议。 Mappable 有个必须要实现的init?(map: Map) 方法,BaseMappable必须要实现 mutating func mapping(map: Map)方法。 所以上述的 XXBook 包含了这两个方法。
二、 json转模型时 XXBook(JSON: json) 调用的方法
  • 调用 XXBook(JSON: json) 时其实调用了BaseMappable协议中默认实现了的方法 public init?(JSON: [String: Any], context: MapContext? = nil)

  • 上面那个方法又会调用 Mapper(context: context).map(JSON: JSON) 这个方法顺利的话会返回一个对象,即我们需要的book对象

  • 返回对象的主要代码时Mapper 类里面的

public func map(JSON: [String: Any]) -> N? {
        let map = Map(mappingType: .fromJSON, JSON: JSON, context: context, shouldIncludeNilValues: shouldIncludeNilValues)
        
        if let klass = N.self as? StaticMappable.Type { // Check if object is StaticMappable
            if var object = klass.objectForMapping(map: map) as? N {
                object.mapping(map: map)
                return object
            }
        } else if let klass = N.self as? Mappable.Type { // Check if object is Mappable
            if var object = klass.init(map: map) as? N {
                object.mapping(map: map)
                return object
            }
        } else if let klass = N.self as? ImmutableMappable.Type { // Check if object is ImmutableMappable
            do {
                return try klass.init(map: map) as? N
            } catch let error {
                #if DEBUG
                let exception: NSException
                if let mapError = error as? MapError {
                    exception = NSException(name: .init(rawValue: "MapError"), reason: mapError.description, userInfo: nil)
                } else {
                    exception = NSException(name: .init(rawValue: "ImmutableMappableError"), reason: error.localizedDescription, userInfo: nil)
                }
                exception.raise()
                #endif
            }
        } else {
            // Ensure BaseMappable is not implemented directly
            assert(false, "BaseMappable should not be implemented directly. Please implement Mappable, StaticMappable or ImmutableMappable")
        }
        
        return nil
    }
  • XXBook类型遵守的是Mappable协议,所以这里的核心代码是
// 初始化一个map
let map = Map(mappingType: .fromJSON, JSON: JSON, context: context, shouldIncludeNilValues: shouldIncludeNilValues)

以及

else if let klass = N.self as? Mappable.Type { // Check if object is Mappable
//重要代码: 根据map初始化了一个object对象,N范型其实这里对应的就是XXBook
    if var object = klass.init(map: map) as? N {
//重要代码:根据map。 对object中的属性赋值
        object.mapping(map: map)
        return object
    }
}
  • 看看object.mapping(map: map)干了什么
mutating func mapping(map: Map) {
        title            <- map["title"]
        author           <- map["author"]
        rating           <- map["rating"]
    }

Operators.swift中可以看到定义了<- 操作符, 可选类型会走下面这个方法

/// Optional object of basic type
// inout 类似于指针,这里是想改变操作符左边的值。
public func <- (left: inout T?, right: Map) {
    switch right.mappingType {
    case .fromJSON where right.isKeyPresent:
        FromJSON.optionalBasicType(&left, object: right.value())
    case .toJSON:
        left >>> right
    default: ()
    }
}

可以看到这里的重要代码是

    class func optionalBasicType(_ field: inout FieldType?, object: FieldType?) {
        field = object
    }

对这里就是赋值了。类似于这种赋值,object.name = map["name"]。 当然,map["name"] 肯定还包含一些内容,毕竟值从哪儿来的还没看到。

  • 上一步中初始化过一个map, 这个map包含里储存了JSON的值。 并且定义了下标函数, 即调用map["name"]时会走Map.swift中的 public subscript(key: String) -> Map 方法。 这个方法调用的核心方法是:
    
    private func `subscript`(key: String, nested: Bool? = nil, delimiter: String = ".", ignoreNil: Bool = false) -> Map {
        // save key and value associated to it
        currentKey = key
        keyIsNested = nested ?? key.contains(delimiter)
        nestedKeyDelimiter = delimiter
        
        if mappingType == .fromJSON {
            // check if a value exists for the current key
            // do this pre-check for performance reasons
            if keyIsNested {
                // break down the components of the key that are separated by delimiter
                (isKeyPresent, currentValue) = valueFor(ArraySlice(key.components(separatedBy: delimiter)), dictionary: JSON)
            } else {
// 如果不包含内嵌的模型,那么获取key值的重要语句就在这里了
                let object = JSON[key]
                let isNSNull = object is NSNull
                isKeyPresent = isNSNull ? true : object != nil
                currentValue = isNSNull ? nil : object
            }
            
            // update isKeyPresent if ignoreNil is true
            if ignoreNil && currentValue == nil {
                isKeyPresent = false
            }
        }
        
        return self
    }
  • 根据map["name"]传进来的key, 获取到当前的currentValue
  • 一一为每个mapping方法中的属性赋值,完了之后返回一个对象。 整个过程就结束了。
if var object = klass.init(map: map) as? N {
//重要代码:根据map。 对object中的属性赋值
        object.mapping(map: map)
        return object
    }

以上就是JSON转模型的全部过程。

探索

  • 上篇提到,json返回的ratingString类型或者Null时,属性会赋值为nil
//定义的 <- 操作符调用的 right.value()
public func <- (left: inout T?, right: Map) {
    switch right.mappingType {
    case .fromJSON where right.isKeyPresent:
        FromJSON.optionalBasicType(&left, object: right.value())
    case .toJSON:
        left >>> right
    default: ()
    }
}

right.value() 调用了Map.swiftvalue() -> T?方法, 方法中的let value = currentValue as? T 类型不对value只能是nil了

public func value() -> T? {
        let value = currentValue as? T  
        
        // Swift 4.1 breaks Float casting from `NSNumber`. So Added extra checks for `Float` `[Float]` and `[String:Float]`
        if value == nil && T.self == Float.self {
            if let v = currentValue as? NSNumber {
                return v.floatValue as? T
            }
        } else if value == nil && T.self == [Float].self {
            if let v = currentValue as? [Double] {
                #if swift(>=4.1)
                return v.compactMap{ Float($0) } as? T
                #else
                return v.flatMap{ Float($0) } as? T
                #endif
            }
        } else if value == nil && T.self == [String:Float].self {
            if let v = currentValue as? [String:Double] {
                return v.mapValues{ Float($0) } as? T
            }
        }
        return value
    }

你可能感兴趣的:(ObjectMapper 源码阅读笔记)