这是我参与更文挑战的第17天,活动详情查看: 更文挑战
SwiftyJSON 是一个 Swift 库,用于更好的处理 JSON 数据。它的核心代码+注释也就 1400 行左右。
下面是我在阅读源码过程中的收获。
实现原理
SwiftyJSON 的核心是一个 struct: JSON。JSON 定义的所有类型为:
public enum Type: Int {
case number
case string
case bool
case array
case dictionary
case null
case unknown
}
对所有支持的类型,在 JSON 内部都封装了一个相应类型的属性来保存值。在修改值时,还会保存值对应的类型。获取值时,就会根据不同的类型读取不同的属性。
JSON 实现了 Swift.Collection 协议,所以我们可以像集合一样使用 JSON。在其内部就是根据不同的类型,调用相应属性(数组或者字典)的方法。
extension JSON: Swift.Collection {
// 根据不同的类型,调用相应的属性(rawArray 或者 rawDictionary)的方法
}
判断 Any 的具体类型
之前我在判断 Any 对应的准备类型时,使用的是 if let data = object as? Data {},多种类型时,就会有多个 if let。SwiftyJSON 中使用的是 switch case,感觉更加的清晰。
public init(_ object: Any) {
switch object {
case let object as Data:
// object 为 Data 类型
default:
self.init(jsonObject: object)
}
}
NSNumber 是由 Bool 还是数值创建的?
NSNumber 可以通过 Bool 值创建,也可以通过各种类型的数值创建。创建完成之后,如何知道 NSNumber 是由 Bool 还是数值创建的?
我之前没有思考过这个问题,总是在需要 Bool 时直接转成 Bool,需要数值时直接转成数值,并没有准确的去判断 NSNumber 究竟是用来表示哪种类型的数据。
SwiftyJSON 中有一段代码用来判断 NSNumber 是否为 Bool 类型:
private let trueNumber = NSNumber(value: true)
private let falseNumber = NSNumber(value: false)
private let trueObjCType = String(cString: trueNumber.objCType)
private let falseObjCType = String(cString: falseNumber.objCType)
// MARK: - NSNumber: Comparable
extension NSNumber {
fileprivate var isBool: Bool {
let objCType = String(cString: self.objCType)
if (self.compare(trueNumber) == .orderedSame && objCType == trueObjCType) || (self.compare(falseNumber) == .orderedSame && objCType == falseObjCType) {
return true
} else {
return false
}
}
}
先通过 compare() 方法判断是否和 Bool 值创建的两个 NSNumber 常量相等,再判断 objCType 是否一致。
NSNumber 的 objCType 属性返回一个 C 字符串,其中包含 NSNumber 对象中包含的数据的 Objective-C 类型。这个字符串由 @encode() 编译器指令编码。
下面打印出几个 objCType 看看。
let trueObjCType = String(cString: NSNumber(value: true).objCType)
let falseObjCType = String(cString: NSNumber(value: false).objCType)
let intObjCType = String(cString: NSNumber(value: 1).objCType)
let doubleObjCType = String(cString: NSNumber(value: 1.1).objCType)
debugPrint(trueObjCType)
// 输出 "c"
debugPrint(falseObjCType)
// 输出 "c"
debugPrint(intObjCType)
// 输出 "q"
debugPrint(doubleObjCType)
// 输出 "d"
在 Objective-C 中,我们可以通过给 @encode 传入一个类型,来获取代表这个类型的编码的 C 字符串:
char *typeChar1 = @encode(int32_t);
char *typeChar2 = @encode(NSArray);
// typeChar1 = "i", typeChar2 = "{NSArray=#}"
当我们需要知道 NSNumber 的准确类型时,就可以通过 objCType 获取类型了,然后和对应的编码进行比对。
另外
NSValue也有objCType属性,可以帮助我们获取准确的类型。