前言:
上篇文章分析了Mirror的特性
,以及从源码的角度查看了Mirror的构成
,根据Mirror的这个特性思考一个问题,长篇大论的Mirror能干什么
?那就引出了本文的内容Json解析
和Error处理
Json解析
基础的Json解析
定义了一个LGTeacher
类,然后通过一个test
方法来解析t
分析:
-
mirror
成功读取对象
的属性名
和属性值
,可使用字典
存储和输出 -
每个
属性
都会默认
当作对象
来处理,通过mirror.children
可判断当前对象
是否拥有属性
,没有就跳出当前属性
的递归
流程。 这样可保证
所有读取children
的完整层级信息
-
递归
实际是利用栈
特性,直接
或间接
调用自身
实现层级读取
的需求
,递归需要
有明确的终止条件
JSON解析封装
上面只针对一个类的Json解析,如果需要针对通用个类,就要对Json解析进行封装,提取JSON解析协议,然后提供一个默认实现,让类遵守协议
//1、定义一个JSON解析协议
protocol CustomJSONMap {
func jsonMap() -> Any
}
//2、默认实现
extension CustomJSONMap{
func jsonMap() -> Any{
let mirror = Mirror(reflecting: self)
//递归终止条件
guard !mirror.children.isEmpty else {
return self
}
//字典,用于存储json数据
var keyValue: [String: Any] = [:]
//遍历
for children in mirror.children {
if let value = children.value as? CustomJSONMap {
if let keyName = children.label {
//递归
keyValue[keyName] = value.jsonMap()
}else{
print("key是nil")
}
}else{
print("当前-\(children.value)异常")
}
}
return keyValue
}
}
//3、让类遵守协议(注意:类中属性的类型也需要遵守协议,否则无法解析)
class LGTeacher: CustomJSONMap {
var age = 18
var name = "LGCat"
}
//使用
var t = LGTeacher()
print(t.jsonMap())
打印异常:
分析,可看到if let value = children.value as? CustomJSONMap
为false
,其子项目没有遵守CustomJSONMap
协议,顾打印不出来
调整代码,使子节点
也遵守CustomJSONMap
协议
由Json的封装
代码,可看出可以自动的解析
对象,但在发生错误
的情况下我发达到预期的效果,那么Mirror
的方案如何处理错误信息
呢?请看下方的分析
错误处理
Swift
中提供了Error协议
来标识当前应⽤程序发⽣错误
的情况,定义如下:
public protocol Error {
}
Error
是一个空协议,其中没有任何实现,这也就意味着你可以遵守该协议,然后自定义错误类型。所以不管是我们的struct、Class、enum
,我们都可以遵循
这个Error
来表示一个错误.
所以接下来,采用enum
方式对上面封装的JSON解析
修改其中的错误处理.
自定义一个枚举类型
的错误枚举
,让其遵守Error协议,
将默认实现的print
替换成枚举
类型
//定义错误类型
enum JSONMapError: Error{
case emptyKey
case notConformProtocol
}
//1、定义一个JSON解析协议
protocol CustomJSONMap {
func jsonMap() -> Any
}
//2、提供一个默认实现
extension CustomJSONMap{
func jsonMap() -> Any{
let mirror = Mirror(reflecting: self)
//递归终止条件
guard !mirror.children.isEmpty else {
return self
}
//字典,用于存储json数据
var keyValue: [String: Any] = [:]
//遍历
for children in mirror.children {
if let value = children.value as? CustomJSONMap {
if let keyName = children.label {
//递归
keyValue[keyName] = value.jsonMap()
}else{
return JSONMapError.emptyKey
}
}else{
return JSONMapError.notConformProtocol
}
}
return keyValue
}
}
由上面的代码中的jsonMap
方法,返回值是Any
类型,但是JSONMapError
协议无法确认返回值是什么,那么该如何区分呢?即如何抛出错误
呢?在这里可以通过throw关键字
(即将Demo中原本return
的错误,改成throw
抛出),jsonMap方法中递归调用,所以还需要在递归调用前增加 try
关键字,最终完整的错误表达方式如下:
extension CustomJSONMap{
func jsonMap() throws -> Any{
let mirror = Mirror(reflecting: self)
//递归终止条件
guard !mirror.children.isEmpty else {
return self
}
//字典,用于存储json数据
var keyValue: [String: Any] = [:]
//遍历
for children in mirror.children {
if let value = children.value as? CustomJSONMap {
if let keyName = children.label {
//递归
keyValue[keyName] = try value.jsonMap()
}else{
throw JSONMapError.emptyKey
}
}else{
throw JSONMapError.notConformProtocol
}
}
return keyValue
}
}
var t = LGTeacher()
print(try t.jsonMap())
参考资料:《Swift值的关键字throw和rethrows》
swift中错误处理的方式
方式一 :try
使用try关键字
,是最简便的,即甩锅,将这个抛出给别人(向上抛出,抛给上层函数),在使用时,需要注意以下两点:
try?
返回一个可选类型
,只有两种结果:
A: 要么成功
,返回具体字典值
B:要么错误
,但并不关心是哪种错误,统一返回nil
try!
使用你绝对
自信的认为这行代码是对的,这行代码绝对
不会发生错误.
方式二:do...catch
可在《Swift值的关键字throw和rethrows》中获得答案
LocalizedError协议
如果只是用Error还不足以表达更详尽的错误信息,可以使用LocalizedError
协议,其定义如下:
此协议遵守Error协议,对JSONMapError
枚举增加一个扩展,遵守LocalizedError
协议,可以打印更详细的错误信息
//定义更详细的错误信息
extension JSONMapError: LocalizedError{
var errorDescription: String?{
switch self {
case .emptyKey:
return "key为空"
case .notConformProtocol:
return "没有遵守协议"
}
}
}
CustomNSError协议
CustomNSError
相当于OC中的NSError
具体的代码实现:
//CustomNSError相当于NSError
extension JSONMapError: CustomNSError{
var errorCode: Int{
switch self {
case .emptyKey:
return 404
case .notConformProtocol:
return 504
}
}
}
总结
-
Swift中处理错误信息的基础协议是
Error
协议,其中LocalizedError
和CustomNSError
都遵守了Error
协议 -
处理错误信息的方法有
do...catch
和try
方法 -
需要同时由
返回正常值
和错误信息
,需要通过throw
关键字配合使用,在方法的返回值箭头前必须要加入throws
关键字,否则throw
关键字会报错
-
如果没有很自信的
确认代码没有问题
,请不要
使用try!
方式处理异常信息 -
在使用
try?
的过程中只有两种
结果要么是nil
,要么是成功