小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
前言
目前Swift中主流的JSON转Model基本上已经被Codable协议占领,这种原生、简单的方式,使得JSON转Model基本上成为了CV活。
不过这也不影响我们学习之前代码中的精华,这次分享的是我之前阅读SwiftyJSON源码的一点心得。
其实SwiftJSON源码一共加起来也就不到1500行,我翻来覆去的看了几次,这里只是将自己觉得可以加以利用的地方列出来。
CustomNSError协议的使用
其实这个协议是继承自协议Error的,需要注意的是这个协议CustomNSError更有利于将Error向NSError转换,并进行as使用。
我们先看看CustomNSError协议是怎么定义的:
public protocol CustomNSError : Error {
/// The domain of the error.
static var errorDomain: String { get }
/// The error code within the given domain.
var errorCode: Int { get }
/// The user-info dictionary.
var errorUserInfo: [String : Any] { get }
}
SwiftJSON的源码如下:
public enum SwiftyJSONError: Int, Swift.Error {
case unsupportedType = 999
case indexOutOfBounds = 900
case elementTooDeep = 902
case wrongType = 901
case notExist = 500
case invalidJSON = 490
}
extension SwiftyJSONError: CustomNSError {
/// return the error domain of SwiftyJSONError
public static var errorDomain: String { return "com.swiftyjson.SwiftyJSON" }
/// return the error code of SwiftyJSONError
public var errorCode: Int { return self.rawValue }
/// return the userInfo of SwiftyJSONError
public var errorUserInfo: [String: Any] {
switch self {
case .unsupportedType:
return [NSLocalizedDescriptionKey: "It is an unsupported type."]
case .indexOutOfBounds:
return [NSLocalizedDescriptionKey: "Array Index is out of bounds."]
case .wrongType:
return [NSLocalizedDescriptionKey: "Couldn't merge, because the JSONs differ in type on top level."]
case .notExist:
return [NSLocalizedDescriptionKey: "Dictionary key does not exist."]
case .invalidJSON:
return [NSLocalizedDescriptionKey: "JSON is invalid."]
case .elementTooDeep:
return [NSLocalizedDescriptionKey: "Element too deep. Increase maxObjectDepth and make sure there is no reference loop."]
}
}
}
大家注意看这里:enum SwiftyJSONError: Int, Swift.Error
。
在定义SwiftyJSONError的是这个枚举继承了Int
,其实准确来说是定义枚举的rawValue为Int
类型。
为何?
因为遵守CustomNSError协议,必须返回errorCode这个属性,如果枚举的rawValue为
Int
类型,那么就可以进行无缝的使用。
这里是SwiftyJSON在编写中的一个小技巧。
通过switch 来替换 as用法
一般当有个对象我们需要转换类型的使用我一般是这么使用的
func toNSURL(url: Any) -> NSURL? {
if url is NSURL {
return url as! NSURL
}
if url is URL {
return (url as! URL) as NSURL
}
if url is String {
return NSURL(string: (url as! String))
}
return nil
}
其实这个完全是可以通过switch来进行处理
func asNSURL(any: Any) -> NSURL? {
switch any {
case let nsURL as NSURL:
return nsURL
case let url as URL:
return url as NSURL
case let string as String:
return NSURL(string: string)
default:
return nil
}
}
少一些强制解包,少一些不安全因素,少一些烦恼。
源码出处,其中这里面还包含了在map函数的递归调用,并且SwiftJSON中多处这样进行了使用:
private func unwrap(_ object: Any) -> Any {
switch object {
case let json as JSON:
return unwrap(json.object)
case let array as [Any]:
return array.map(unwrap)
case let dictionary as [String: Any]:
var unwrappedDic = dictionary
for (k, v) in dictionary {
unwrappedDic[k] = unwrap(v)
}
return unwrappedDic
default:
return object
}
}
NSNumber的比较处理
这个源码处理的比较详细,完全可以copy一份在自己的分类中使用,因为这个分类在SwiftJSON是private
的,所以基本上不用担心copy一份引起冲突:
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
}
}
}
func == (lhs: NSNumber, rhs: NSNumber) -> Bool {
switch (lhs.isBool, rhs.isBool) {
case (false, true):
return false
case (true, false):
return false
default:
return lhs.compare(rhs) == .orderedSame
}
}
func != (lhs: NSNumber, rhs: NSNumber) -> Bool {
return !(lhs == rhs)
}
func < (lhs: NSNumber, rhs: NSNumber) -> Bool {
switch (lhs.isBool, rhs.isBool) {
case (false, true):
return false
case (true, false):
return false
default:
return lhs.compare(rhs) == .orderedAscending
}
}
func > (lhs: NSNumber, rhs: NSNumber) -> Bool {
switch (lhs.isBool, rhs.isBool) {
case (false, true):
return false
case (true, false):
return false
default:
return lhs.compare(rhs) == ComparisonResult.orderedDescending
}
}
func <= (lhs: NSNumber, rhs: NSNumber) -> Bool {
switch (lhs.isBool, rhs.isBool) {
case (false, true):
return false
case (true, false):
return false
default:
return lhs.compare(rhs) != .orderedDescending
}
}
func >= (lhs: NSNumber, rhs: NSNumber) -> Bool {
switch (lhs.isBool, rhs.isBool) {
case (false, true):
return false
case (true, false):
return false
default:
return lhs.compare(rhs) != .orderedAscending
}
}
这段代码比较巧妙的通过NSNumber的扩展,通过编写运算符来表示两个NSNumber对象的相等、不等、大于、小于、大于等于、小于等于几种情况。
并且将switch的用法展现得淋漓尽致,并且将NSNumber的比较分成两大情况:NSNumber的bool值比较
和NSNumber的值的大小比较
。
总结
阅读源码都是很难的,并且有的时候都是全程的看不懂看不懂,不如从某一点出发,能看懂一点就是收获,记录下来,就有成就感了。
我们下期见。