美文网首页
swift mirror & error

swift mirror & error

作者: f8d1cf28626a | 来源:发表于2022-07-28 23:49 被阅读0次

    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<Mirror.Child>
    
    //进入Child,发现是一个元组类型,由可选的标签和值构成,
    public typealias Child = (label: String?, value: Any)
    

    这也是为什么能够通过label、value打印的原因。即可以在编译时期且不用知道任何类型信息情况下,在Child的值上用Mirror去遍历整个对象的层级视图

    JSON解析

    根据Mirror的这个特性,我们思考下,可以通过Mirror做什么?首先想到的是JSON解析,如下所示,我们定义了一个Rocteacher类,然后通过一个test方法来解析t

    
      /**
         JSON解析
         */
        fileprivate func analyzingJson<T>(_ 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

          本文链接:https://www.haomeiwen.com/subject/lhlcwrtx.html