SwiftyJSON 源码学习

jopen 6年前

原文 http://www.jianshu.com/p/a9bdbd1255b9

SwiftyJSON 源码学习

SwiftyJSON 是一个很优秀 Swift 语言第三方库。我们在之前的文章中对它有过介绍。相信大家对它也有了一些了解。提升开发功力最好的方式就是学习优秀的源代码了,记得大神 TJ Holowaychuk 也这么说过。所以我们这次一起来学习一下 SwiftyJSON 的代码。

SwiftyJSON 很适合我们做源码研究。首先,它的代码量很少,整个库只有一个代码文件。这样我们就能很快的了解它的整体结构。

另外,虽然它的代码量不大,但是却很充分的用到了 Swift 的特性。通过研究它,能帮助我们着切实的了解 Swift 以及这些特性的应用场景。

开始之前

首先呢,在学习 SwiftyJSON 代码之前,最好先了解一下怎么使用它。关于 SwiftyJSON 的使用,我们之前这篇文章中有讨论,如果大家之前没有使用过 SwiftyJSON 可以先参考这里: 使用 SwiftyJSON 处理 JSON 解析

踏上发现之旅

那么我们开始吧。

首先,我们打开 SwiftyJSON 的项目文件,就可以看到它的结构啦:

SwiftyJSON 源码学习

非常简单,只有两个文件SwiftyJSON.h和SwiftyJSON.swift。

其中SwiftyJSON.h文件中,只包含了两个定义 :

FOUNDATION_EXPORT double SwiftyJSONVersionNumber;    FOUNDATION_EXPORT const unsigned char SwiftyJSONVersionString[];

分别用作表示 SwiftyJSON 的版本号。主要的代码,就都集中在SwiftyJSON.swift这个文件中了:

接下来,我们开始分析主体代码吧,SwiftyJSON 中只有一个类 -JSON,确切的说JSON不是一个class而是一个struct,看看它的定义就知道了:

public struct JSON {      // some code      public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) {      // some code    }      public init(_ object: AnyObject) {        // some code    }      public init(_ jsonArray:[JSON]) {        // some code    }      public init(_ jsonDictionary:[String: JSON]) {        // some code    }    }

确实,它是一个struct, 关于struct的特性,可以参看瞄神的这篇: 将 PROTOCOL 的方法声明为 MUTATING

并且定义了 4 个构造方法,咱们来一一看一下这几个构造方法的实现:

public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) {   do {    let object: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: opt)    self.init(object)   } catch let aError as NSError {    if error != nil {     error.memory = aError    }    self.init(NSNull())   }  }

第一个构造方法,允许我们用NSData来构建JSON对象,这个方法也是最常用到得。它接受 3 个参数。其中 data 参数是传递进来用于解析的 JSON 数据, options 是读取选项,error 是错误指针。

其中 options 和 error 这两个参数已经在定义的时候指定了默认值,所以我们调用的时候,只有 data 参数是必须传递进来的,其他两个参数都是可选的。

我们再看这个构造方法内部的定义,一目了然,其实SwiftJSON内部还是用到了NSJSONSerialization来解析 JSON 数据。

解析完成之后,如果解析成功,就会调用另外一个构造方法self.init(object)来继续初始化。

如果解析失败,则调用self.init(NSNull())这个构造方法进行继续的初始化。

再看下一个构造方法:

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

这个方法接受的参数,也就是第一个构造方法中使用NSJSONSerialization解析后返回的 object 对象。处理也非常简单,只是将解析后的 object 对象保存到这个类的属性中。

接下来再继续看:

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

这个构造方法就比较有意思了,它接受的是一个JSON对象的数组,里面用了一个map方法jsonArray.map { $0.object }。 这个方法做的事情其实就是将 JSON 对象,的数组转换成一般对象的数组。

还记得上一个构造方法的 self.object 吗,这里面存放的其实就是NSJSONSerialization解析后的 JSON 数据。注意看 map 方法里面的实现:

jsonArray.map { $0.object }

其实这个方法,用一个更加详细的写法,就等同于这个:

jsonArray.map { element in      return element.object  }

咱们一点点分析,先说一下map方法,其实这个方法就是对数组中得元素进行一次变换处理,将每一个数组元素都替换成map调用的闭包中返回的值。

我们这个例子中的闭包,返回的就是return element.object这个值。也就是说,这个方法其实是把原本还有JSON对象类型的数组里面的所有元素,都替换成了,JSON里面包含的 object 的值。

这样经过这个map操作,jsonArray这个数组的类型已经发生变化了。

而 SwiftyJSON 使用的这种方式 -jsonArray.map { $0.object }是一种简写形式,$0代表的就是遍历的每一个元素,相当于element,而闭包有一个特性,如果没有显示的制定return语句,那么就将最后一个表达式执行的结果作为返回值。

恩,神奇的 Swift~

map调用结束后,我们又将它传入了另外一个构造方法:

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

因为 jsonArray 是一个 Array, Array 在 JSON 类所定义的 4 个构造方法中,能匹配这个参数的方法就只有这个了:

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

哈哈,最后又回到咱们上一个讨论的方法啦,将 jsonArray.map 调用后的返回值,赋值给 object 属性。

其实就是相当于,将一个JSON类型的数组,转换成普通 JSON 对象(注意:这两个 JSON 的含义不同,前面那个 JSON 表示的是 SwiftyJSON 中所定义的 JSON 对象,而后面那个 JSON,表示的是 JSON 数据的意思。谨记谨记~)。

那么咱们继续,来看看最后一个构造方法:

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

这个方法接收的是一个字典类型的参数,这个字段的键和值分别是String和JSON类型。构造方法的实现中,对jsonDictionary这个字典进行遍历,然后将JSON类的 object 属性,以相同的key值生成了一个新的字典,然后再次用这个新的字典调用构造方法:self.init(dictionary)

相信聪明的各位同学,已经看出来了。这个构造方法和之前那个调用jsonArray.map { $0.object }的构造方法如出一辙。

只不过这个是将字典中的JSON对象,做了一次拆包。将包含JSON对象类型的字典,转换成包含普通对象的字典。

最后,self.init(dictionary)依然调用的是那个接受AnyObject的构造方法。又构建了一个将jsonDictionary转换后的普通字典作为 object 属性值得 JSON 对象。

回顾总结

读一读源码,是不是会有不少发现呢,我们看完JSON类的这几个构造方法,其实就已经基本摸清 SwiftyJSON 的大致架构了。

从第一个构造方法中的解析方式得知,SwiftyJSON 的内部依然依赖于NSJSONSerialization机制来解析 JSON 数据。

它的内部,使用object属性,来维护NSJSONSerialization所解析出来的原始内容。

NSJSONSerialization解析出来的内容,一般是NSArray或者NSDictionary类型,分别对应 JSON 数据中的[...]和{...},并且NSArray和NSDictionary集合中存储的值都是 JSON 原生值类型。

另外,从这两个构造方法也可知 SwiftyJSON 的 JSON 对象的一些设计思路:

public init(_ jsonArray:[JSON]) {      // some code  }    public init(_ jsonDictionary:[String: JSON]) {      // some code  }

这两个构造方法接受的都是JSON类型的集合,但最终会将这些集合中的原始数据合并成一个新的集合,然后设置到新构造的 JSON 对象中。

分析了一下 SwiftyJSON 的代码,是不是觉得有一些收获呢?我们从中得到了一些设计思路吧。不过,本人水平有限,也许未能分析的面面俱到,就让这篇文章成为一个开始,我们一起学习进步吧。

更多精彩内容可关注微信公众号:

swift-cafe

SwiftyJSON 源码学习
</div> </div>