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



- 作者: SwiftCafe


我们在之前的文章中专门介绍过 SwiftyJSON 库,帮助我们方便的在 Swift 语言中处理 JSON 数据。SwiftyJSON 是一个开源的第三方库,它的代码我们可以在 github 上面找到。并且 SwiftyJSON 的代码,可以称得上短小精悍。
只有短短的 1000 多行代码。但虽然只有这么段的代码,却涵盖了一个第三方库该有的大部分编码标准,看完这篇文章后,你的功力就会大幅提升。为什么呢?

答案很简单,就是你可以用 API 设计者的思维来思考问题啦。这种思维角度非常的重要,

了解 SwiftyJSON

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

https://github.com/SwiftyJSON/SwiftyJSON

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

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

JSON 类定义

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 来定义的:

1
public struct JSON

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

比如这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
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 会有什么行为呢:

1
2
3
4
5
6
7
8
9
10
11
12
13
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 作为参数的这个:

1
2
3
4
5
6
7
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 是可选的,这样我们就可以用这种方式来使用这个构造方法:

1
let jsonObj = JSON(data:someData)

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 :

1
2
3
get {
return _object
}

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 变量的定义:

1
private var _type: Type = .Null

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

1
2
3
4
5
6
7
8
9
10
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 对象的构造流程就完成了。

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

1
2
3
4
5
6
7
8
9
10
11
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)
}

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

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

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

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

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

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

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

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

1
jsonArray.map { $0.object }

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

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

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

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

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

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

1
2
3
4
5
6
7
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
邮件订阅
请输入您的邮箱,我们会把最新的内容推送给您: