swift mirror & error

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解析代码

  1. 创建协议并添加方法
protocol CustomJson{
    func jsonMap3()throws -> Any
}
  1. 创建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
        }
    }
    
}
  1. 实现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
    }
}
  1. 创建 RocmapJsonObject
// 创建 RocmapJsonObject
class RocmapJsonObject:CustomJson {
    let age = 18
    let name = "roc"
}
  1. 使用 RocmapJsonObject
 // JSON封装类使用
 let mapJsonObject = RocmapJsonObject()
 do {
  let mapJsonResult3 = try mapJsonObject.jsonMap3()
  print(mapJsonResult3)
 }
 catch {
  print((error as! JsonMapErrors).errorCode)
 }

你可能感兴趣的:(swift mirror & error)