swift Mirror
Mirror
Mirror 是指可以动态获取类型、成员信息,在运行时可以调用方法、属性等行为的特性
- 对于一个纯swift类来说,并不支持直接像OC runtime那样的操作
- 但是swift标准库依旧提供了反射机制,用来访问成员信息,即Mirror
创建一个类 Rocteacher
class Rocteacher {
var age = 18
}
创建解析方法
fileprivate func mirrorTesting()->Void{
// 应该传入实例对象
let t = Rocteacher()
let mirror = Mirror(reflecting: t.self)
for pro in mirror.children {
print("\(pro.label ?? "hello") : \(pro.value)")
}
}
// 使用
mirrorTesting()
// 结果
age : 18
- 查看Mirror定义
进入Mirror初始化方法,发现传入的类型是Any,则可以直接传t
public init(reflecting subject: Any)
进入children
public let children: Mirror.Children
//进入Children,发现是一个AnyCollection,接收一个泛型
public typealias Children = AnyCollection
//进入Child,发现是一个元组类型,由可选的标签和值构成,
public typealias Child = (label: String?, value: Any)
这也是为什么能够通过label、value打印的原因。即可以在编译时期且不用知道任何类型信息情况下,在Child的值上用Mirror去遍历整个对象的层级视图
JSON解析
根据Mirror的这个特性,我们思考下,可以通过Mirror做什么?首先想到的是JSON解析,如下所示,我们定义了一个Rocteacher类,然后通过一个test方法来解析t
/**
JSON解析
*/
fileprivate func analyzingJson(_ obj : T) -> Any{
let mirror = Mirror(reflecting: obj)
guard !mirror.children.isEmpty else {return obj}
// 创建字典
var keyValue = [String:Any]()
for children in mirror.children {
if let keyName = children.label {
keyValue[keyName] = (analyzingJson(children.value) as Any)
}
else {
print("children.label 为空")
}
}
return keyValue
}
// 使用
let t = Rocteacher()
let obj = analyzingJson(t.self)
print(obj)
// 结果
["age": 18]
JSON解析封装
创建一个protocol
protocol CustomJson {
func jsonMap() -> Any
}
extension CustomJson 实现协议方法(注意 不能用fileprivate,否则外部文件中不能调用)
extension CustomJson {
func jsonMap() -> Any {
let mirror = Mirror(reflecting: self)
// 递归终止条件
guard !mirror.children.isEmpty else {return self}
// 创建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
print("keyName == nil")
}
}
else{
print("当前-\(children.value)-没有遵守协议")
}
}
return mapObject
}
}
创建一个遵循 CustomJson(protocol) 的类
// 创建
class RocmapJsonObject:CustomJson {
let age = 18
let name = "roc"
}
使用 RocmapJsonObject
// JSON封装类使用
let mapJsonObject = RocmapJsonObject()
let mapJsonResult = mapJsonObject.jsonMap()
print(mapJsonResult)
// 结果
当前-18-没有遵守协议
当前-roc-没有遵守协议
解决 遵守协议的问题
extension Int:CustomJson{}
extension String:CustomJson{}
再次使用 RocmapJsonObject
// JSON封装类使用
let mapJsonObject = RocmapJsonObject()
let mapJsonResult = mapJsonObject.jsonMap()
print(mapJsonResult)
// 结果
["age": 18, "name": "roc"]
Error协议
Swift中,提供了Error协议来标识当前应用程序发生错误的情况
Error协议
public protocol Error { // 这家伙本身就是一个协议}
Error是一个空协议,其中没有任何实现,这也就意味着你可以遵守该协议,然后自定义错误类型。所以不管是我们的struct、Class、enum,我们都可以遵循这个Error来表示一个错误
所以接下来,对我们上面封装的JSON解析修改其中的错误处理
使用 enum 定一个错误类型 遵循Error
enum JsonMapError:Error {
case Emptykey
case NotConformProtocol
}
首先--->注释掉 Int,String 遵循CustomJson的定义
//extension Int:CustomJson{}
//extension String:CustomJson{}
方式一
1.修改 jsonMap (return)
extension CustomJson {
func jsonMap() -> Any {
let mirror = Mirror(reflecting: self)
// 递归终止条件
guard !mirror.children.isEmpty else {return self}
// 创建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
return JsonMapError.Emptykey
}
}
else{
// print("当前-\(children.value)-没有遵守协议")
return JsonMapError.NotConformProtocol
}
}
return mapObject
}
}
2.查看使用结果
// JSON封装类使用
let mapJsonObject = RocmapJsonObject()
let mapJsonResult = mapJsonObject.jsonMap()
print(mapJsonResult)
// 结果
NotConformProtocol
方式二
1.修改 jsonMap (throws)
extension CustomJson {
func jsonMap2()throws -> Any {
let mirror = Mirror(reflecting: self)
// 递归终止条件
guard !mirror.children.isEmpty else {return self}
// 创建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
throw JsonMapError.Emptykey
}
}
else{
// print("当前-\(children.value)-没有遵守协议")
throw JsonMapError.NotConformProtocol
}
}
return mapObject
}
}
2.查看使用结果
let mapJsonObject = RocmapJsonObject()
let mapJsonResult2 = try! mapJsonObject.jsonMap2()
print(mapJsonResult2 as Any)
// 结果
ViewController.swift:29: Fatal error: 'try!' expression unexpectedly raised an error: Swift_Case_1.(unknown context at $10ca3e338).JsonMapError.NotConformProtocol
try
使用try关键字,是最简便的,即甩锅,将这个抛出给别人(向上抛出,抛给上层函数)。但是在使用时,需要注意以下两点:
-
try? 返回一个可选类型,只有两种结果:
- 要么成功,返回具体的字典值
- 要么错误,但并不关心是哪种错误,统一返回nil
try! 表示你对这段代码有绝对的自信,这行代码绝对不会发生错误 或 绝对会发生错误
do ... catch
通过do-catch来处理JSON解析的错误
1.jsonMap2
extension CustomJson {
func jsonMap2()throws -> Any {
let mirror = Mirror(reflecting: self)
// 递归终止条件
guard !mirror.children.isEmpty else {return self}
// 创建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
throw JsonMapError.Emptykey
}
}
else{
// print("当前-\(children.value)-没有遵守协议")
throw JsonMapError.NotConformProtocol
}
}
return mapObject
}
}
2.查看使用结果
do {
let mapJsonResult3 = try mapJsonObject.jsonMap2()
print(mapJsonResult3)
}
catch {
print(error)
}
// 结果
NotConformProtocol
LocalError协议
如果只是用Error还不足以表达更详尽的错误信息,可以使用LocalizedError协议
其定义如下
public protocol LocalizedError : Error {
/// A localized message describing what error occurred.错误描述
var errorDescription: String? { get }
/// A localized message describing the reason for the failure.失败原因
var failureReason: String? { get }
/// A localized message describing how one might recover from the failure.建议
var recoverySuggestion: String? { get }
/// A localized message providing "help" text if the user requests help.帮助
var helpAnchor: String? { get }
}
实现
1.jsonMap2
extension CustomJson {
func jsonMap2()throws -> Any {
let mirror = Mirror(reflecting: self)
// 递归终止条件
guard !mirror.children.isEmpty else {return self}
// 创建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
throw JsonMapError.Emptykey
}
}
else{
// print("当前-\(children.value)-没有遵守协议")
throw JsonMapError.NotConformProtocol
}
}
return mapObject
}
}
2.extension JsonMapError:LocalizedError
extension JsonMapError:LocalizedError {
// 重写 errorDescription
var errorDescription: String? {
switch self{
case .Emptykey: return "key == nil"
case .NotConformProtocol: return "没有遵循协议"
}
}
}
3.查看使用结果
// 方式四
do {
let mapJsonResult3 = try mapJsonObject.jsonMap2()
print(mapJsonResult3)
}
catch {
print(error.localizedDescription)
}
// 结果
没有遵循协议
CustomNSError协议
CustomNSError相当于OC中的NSError
其定义如下,有三个默认属性
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 }
}
实现
1.创建一个enum 遵循Error 和 CustomNSError
enum JsonMapErrors:Error,CustomNSError{
case Emptykey
case NotConformProtocol
var errorCode: Int {
switch self {
case .Emptykey: return 404
case .NotConformProtocol: return 520
}
}
}
2.jsonMap3
extension CustomJson {
func jsonMap3()throws -> Any {
let mirror = Mirror(reflecting: self)
// 递归终止条件
guard !mirror.children.isEmpty else {return self}
// 创建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
throw JsonMapErrors.Emptykey
}
}
else{
// print("当前-\(children.value)-没有遵守协议")
throw JsonMapErrors.NotConformProtocol
}
}
return mapObject
}
}
3.查看使用结果
// 方式五
do {
let mapJsonResult3 = try mapJsonObject.jsonMap3()
print(mapJsonResult3)
}
catch {
print((error as! JsonMapErrors).errorCode)
}
// 结果
520
总结
Error是swift中错误类型的基本协议,其中LocalizedError、CustomNSError都遵守Error
- 如果在方法中,想要同时返回正常值和错误,需要通过throw返回错误,并且在方法返回值的箭头前面加throws关键字,再调用方法时,还需要加上try关键字,或者 使用do-catch
使用try时,有以下两点需要注意:
- try? 返回的是一个可选类型,要么成功,返回正常值,要么失败,返回nil
- try! 表示你对自己的代码非常自信,绝对不会发生错误,一旦发生错误,就会崩溃
使用建议:建议使用try?,而不是try!
最后附上完整的自定义JSON解析代码
- 创建协议并添加方法
protocol CustomJson{
func jsonMap3()throws -> Any
}
- 创建JsonMapErrors
// 创建JsonMapErrors
//extension Int:CustomJson{}
//extension String:CustomJson{}
enum JsonMapErrors:Error,LocalizedError,CustomNSError{
case Emptykey
case NotConformProtocol
var errorDescription: String? {
switch self {
case .Emptykey: return "keyName == nil"
case .NotConformProtocol: return "value 没有遵守协议"
}
}
var errorCode: Int {
switch self {
case .Emptykey: return 404
case .NotConformProtocol: return 520
}
}
}
- 实现jsonMap3协议方法
extension CustomJson {
func jsonMap3()throws -> Any {
let mirror = Mirror(reflecting: self)
// 递归终止条件
guard !mirror.children.isEmpty else {return self}
// 创建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
throw JsonMapErrors.Emptykey
}
}
else{
// print("当前-\(children.value)-没有遵守协议")
throw JsonMapErrors.NotConformProtocol
}
}
return mapObject
}
}
- 创建 RocmapJsonObject
// 创建 RocmapJsonObject
class RocmapJsonObject:CustomJson {
let age = 18
let name = "roc"
}
- 使用 RocmapJsonObject
// JSON封装类使用
let mapJsonObject = RocmapJsonObject()
do {
let mapJsonResult3 = try mapJsonObject.jsonMap3()
print(mapJsonResult3)
}
catch {
print((error as! JsonMapErrors).errorCode)
}