深阅读 - swiftyJSON 源码研究 第一篇

swift 发布于 2017年09月23日
了解 SwiftyJSON

要开始研究 SwiftyJSON 库,我们首先要得到它的代码。大家可以访问 SwiftyJSONGithub 上面的主页来得到它的代码:

https://github.com/SwiftyJSON/SwiftyJSON

我们将代码下载下来后,就可以开始研究它了。

SwiftyJSON 库中,主要对外暴露了一个类,就是 JSON 类。这个类主要负责整体的数据解析和交互逻辑,下面我们就来从这个类看起吧。

JSON 类定义

首先,我们来看一下 JSON 类的初始化方法,这里我们对代码格式做了精简化处理,删除了那些无用的格式:

public struct JSON {

    public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) {
        if let object: AnyObject = NSJSONSerialization.JSONObjectWithData(data, options: opt, error: error) {
            self.init(object)
        } else {
            self.init(NSNull())
        }
    }

    public init(_ object: AnyObject) {
        self.object = object
    }

    public init(_ jsonArray:[JSON]) {
        self.init(jsonArray.map { $0.object })
    }

    public init(_ jsonDictionary:[String: JSON]) {
        var dictionary = [String: AnyObject]()
        for (key, json) in jsonDictionary {
            dictionary[key] = json.object
        }
        self.init(dictionary)
    }

首先,我们看到 JSON 类是使用 struct 来定义的:

public struct JSON

structclassSwift 中定义的类型,都可以包含成员变量和方法,这点和我们在其他语言中对于 struct 的理解略有不同。在 Swift 中,structclass 的区别是,struct 声明的类型的实例,在传值的时候是拷贝传值,而 class 类型的实例在传值的时候是引用传值。

比如这个例子:

struct Person {

    var name:String!

}

var mike:Person = Person()
mike.name = "Mike"

var jake = mike
jake.name = "Jake"

print("\(mike.name) and \(jake.name)")  // Mike and Jake

我们用 struct 定义的 Person 类,在经过引用拷贝后,两个变量都维护着自己的底层属性。那么我们看看同样的情况,换做 class 会有什么行为呢:

class Person {

    var name:String!

}

var mike:Person = Person()
mike.name = "Mike"

var jake = mike
jake.name = "Jake"

print("\(mike.name) and \(jake.name)") //Jake and Jake

大家看到了吧,我们只将 struct 声明改成了 class, 输出的结果和之前产生了明显的不同,两个变量 mikejakename 属性值都是 "Jake" ,这是因为 class 是引用拷贝,虽然我们通过 var jake = mike 将 mike 赋值给了 jake ,但实际上我们复制的只是引用,这就导致这两个变量指向了同样的实例,所以得到这样的输出结果也就不足为奇了。

构造方法

JSON 类一共有 4 个构造方法,我们逐个来分析,首先我们来看用 NSData 作为参数的这个:

    public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) {
        if let object: AnyObject = NSJSONSerialization.JSONObjectWithData(data, options: opt, error: error) {
            self.init(object)
        } else {
            self.init(NSNull())
        }
    }

这个构造方法是 JSON 类对外的主要构造方法,它以 NSData 对象作为参数,并且后两个参数 optionserror 是可选的,这样我们就可以用这种方式来使用这个构造方法:

let jsonObj = JSON(data:someData)

我们仔细看一下这个构造方法,其实它的内部依然用到了 NSJSONSerialization 来解析 JSON 数据。这样我们就大致了解它的运作原理啦,原来它也是基于 NSJSONSerialization 的一个封装。

接下来,我们看一下这个方法的实现,它里面做了一个 if 判断,检测 NSJSONSerialization 进行的 JSON 解析是否成功。如果解析成功,则将解析返回的对象传入 self.init(object) 这另外一个构造方法。如果解析失败,则传入一个 NSNull() 到这个构造方法: self.init(NSNull())

那么我们顺利成章,接下来就看一下 self.init(object) 这个构造方法。

public init(_ object: AnyObject) {
        self.object = object
}

我们看到,这个构造方法的实现也很简单,只是将传入的 object 对象赋值给内部的 self.object 属性。那么我们继续看一下 self.object 属性的定义吧:

public var object: AnyObject {

  get {
    return _object
  }
  set {
    _object = newValue
    switch newValue {
      case let number as NSNumber:
         if number.isBool {
                     _type = .Bool
                     }
         else {
                     _type = .Number
                 }
      case let string as NSString:
              _type = .String
      case let null as NSNull:
              _type = .Null
      case let array as [AnyObject]:
              _type = .Array
      case let dictionary as [String : AnyObject]:
              _type = .Dictionary
      default:
              _type = .Unknown
                   _object = NSNull()
                  _error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType,   userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"])
    }
  }
}

我们看到,object 属性的定义采用了 Swift 的自定义 setget。简而言之就是我们可以自己处理属性的设置和获取的逻辑。这里我们不过多赘述。

get 方法的实现比较简单,只是简单的返回了这个 object :

get {
        return _object
}

set 的方法实现稍微复杂些,首先通过这个语句 _object = newValue 将内部的输入值设置成新的值。newValue 是一个特殊变量,代表 set 方法传递进来的值,比如我们前面的 self.object = object 等于号右边传递进来的值就是 newValue 所代表的值。

接下来,通过一个 switch 语句,判断新传入的值的类型,并相应的把类型信息存储到 _type 变量中:

switch newValue {
    case let number as NSNumber:
         if number.isBool {
             _type = .Bool
             }
         else {
             _type = .Number
         }
    case let string as NSString:
        _type = .String
    case let null as NSNull:
        _type = .Null
    case let array as [AnyObject]:
        _type = .Array
    case let dictionary as [String : AnyObject]:
        _type = .Dictionary
    default:
        _type = .Unknown
            _object = NSNull()
            _error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType,   userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"])
}

还有一个 default 分支,对那些类型不在上述判断内的变量,把他们设置为未知类型。分析到现在,大家是不是还有一个疑问呢,那就是 _type 变量到底是什么东东呢? 我们来看一下 type 变量的定义:

private var _type: Type = .Null

它是一个 Type 类型的变量,那么关于 Type 类型,它的定义又是怎样的呢?

public enum Type :Int{

    case Number
    case String
    case Bool
    case Array
    case Dictionary
    case Null
    case Unknown
}

其实它就是一个枚举啦,它定义了 JSON 对象中可以出现的数据类型,这就和我们前面分析的 switch 判断对应上了把。

分析道这里我们是不是对 JSON 对象的初始化有了一个整体的认识呢?基本的步骤就是这样的:

  1. 首先我们传递 NSData 到 JSON 对象的构造方法。
  2. 这个构造方法会使用 NSJSONSerialization 来解析传入的 NSData
  3. 如果解析成功,会将解析好的对象传入 JOSN 的另外一个接受 AnyObject 类型参数的构造方法。
  4. 这个构造方法会根据传入对象的类型,将它保存到自己的 object 存储,并保存它的类型信息。
  5. 到此 JSON 对象的构造流程就完成了。

这个流程明确了,我们再看一下另外两个构造方法:

public init(_ jsonArray:[JSON]) {
        self.init(jsonArray.map { $0.object })
}

public init(_ jsonDictionary:[String: JSON]) {
        var dictionary = [String: AnyObject]()
        for (key, json) in jsonDictionary {
                dictionary[key] = json.object
        }
        self.init(dictionary)
}

这两个构造方法,其实相当于对整体流程的一个补充,我们先来看第一个,它的实现只有一条语句:

self.init(jsonArray.map { $0.object })

JSON 为值类型的数组,通过 map 函数做一个变换,将他们作为普通值得数组,传递给 public init(_ object: AnyObject) 构造方法。

是不是被这么一说感觉有点蒙呢,没关系我们举个例子说一下就明白啦。

JSON 对象其实是对值得一个封装,他们里面可以封装前面提到的枚举 Type 里面描述的任何一种类型的值。

假如我们有一个数组,里面存储了 5 个 JSON 类型的变量,每个 JSON 类型都封装了一个 String 类型的值。

let array = [JSON("Str1"),JSON("Str2"),JSON("Str3"),JSON("Str4"),JSON("Str5")]

这时,我们的数组其实是上述的这种结构。而 map 函数的作用在于对数组中得元素做一个变化,比如这个调用:

jsonArray.map { $0.object }

$0 代表当前对象,我们这个调用就是将所有的值都变化为 JSON 对象的 object 属性,那么变换后,我们的数组结构就类似于这样:

let array = ["Str1","Str2","Str3","Str4","Str5"]

我们再将这个数组传递给 JSON 的构造方法,实际上就是这个调用:

self.init(jsonArray.map { $0.object })

这下,就又回到我们的整体构造 JSON 的流程了,我们传递一个 Array 类型的变量给 JSON 的构造方法,由于 Array 类型是 Type 枚举中所列举的类型之一,那么 JSON 会将这个 Array 类型的变量设置成它的 object 属性,然后将类型信息保存到 type属性中。

对于最后一个构造方法,以让是同样的逻辑:

public init(_ jsonDictionary:[String: JSON]) {
  var dictionary = [String: AnyObject]()
  for (key, json) in jsonDictionary {
        dictionary[key] = json.object
  }
  self.init(dictionary)
}

这次,我们传入的是一个 JSON 为值类型的字典,我们首先也做了一次转换工作,就是将字典中 JSON 类型的值,转换成了 JSON 对象中所包含的具体的 object 值。随后我们将这个转换后的字典传入 JSON 对象的构造方法。这样 JSON 对象的整体构造流程我们就描述完了。

SequenceType

我们了解完了 JSON 类的构造方法,JSON 类还是实现了 SequenceType 协议。这个协议简单说就是,实现了它的对象可以用 for 循环来进行遍历。

我们来看一下 SequenceType 的定义:



如果你觉得这篇文章有帮助,还可以关注微信公众号 swift-cafe,会有更多我的原创内容分享给你~

本站文章均为原创内容,如需转载请注明出处,谢谢。
关注微信公众号
发现更多精彩
swift-cafe