Swift Runtime探索,Error定义使用

凌蕴藉
2023-12-01

Swift Runtime探索,Error定义使用

我们⽤下⾯这段代码来测试⼀下:

class LGTeacher { 
 var age: Int = 18 
 func teach(){ 
   print("teach") 
 }  
} 
 let t = LGTeacher() 
 func test(){ 
     var methodCount:UInt32 = 0 
     let methodlist = class_copyMethodList(LGTeacher.self, &method Count) 
     for i in 0..<numericCast(methodCount) { 
         if let method = methodlist?[i]{ 
             let methodName = method_getName(method); 
             print("⽅法列表:\(String(describing: methodName))") 
         }else{ 
             print("not found method"); 
         } 
     } 
     var count:UInt32 = 0 
     let proList = class_copyPropertyList(LGTeacher.self, &count) 
     for i in 0..<numericCast(count) {
         if let property = proList?[i]{
             let propertyName = property_getName(property);
             print("属性成员属性:\(String(utf8String: propertyName)! )")
         }else{
             print("没有找到你要的属性"); 
         } 
     } 
 }

运⾏这段代码你会发现,当前不管是我们的⽅法列表还是我们的属性列表,此次此刻都是为空的。

 @objc 的标识,如果这个时候我们将我们当前的⽅法和属性添加上,会发⽣什么?

此刻代码会输出我们当前的 teach ⽅法和 age 属性。但是此刻对于我们的 OC 来说是没有办法使⽤的:
 
 
所以这⾥我们得出来这样⼀个结论:
  •  
    对于纯 Swift 类来说,没有 动态特性。⽅法和属性不加任何修饰符的情况下。这个时候其实已经不具
    备我们所谓的 Runtime 特性了,这和我们的⽅法调度(V-Table调度)是不谋⽽合的。
    dynamic (动态特性)
  •  
    对于纯 Swift 类,⽅法和属性添加 @objc 标识的情况下,当前我们可以通过 Runtime API 拿到,但
    是在我们的 OC 中是没法进⾏调度的
  •  
    对于继承⾃ NSObject 类来说,如果我们想要动态的获取当前的属性和⽅法,必须在其声明前添加
    @objc 关键字,⽅法交换: dynamic的标识。否则也是没有办法通过 Runtime API 获取的 

2. 反射  

反射就是可以动态获取类型、成员信息,在运⾏时可以调⽤⽅法、属性等⾏为的特性。上⾯我们分析过 了,对于⼀个纯 Swift 类来说,并不⽀持我们直接像 OC 那样操作;但是 Swift 标准库依然提供了反射机制让我们访问成员信息, 反射的⽤法⾮常简单,我们⼀起来熟悉⼀下:

     var t = LGTeacher()
     let mirror = Mirror(reflecting: t) 
     for pro in mirror.children{ 
     print("\(pro.label):\(pro.value)") 
 }

那这个时候我们能⽤ Mirror 做些什么事情那?⾸先想到的应该就是 JSON 解析了:

func test(_ obj: Any) -> 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] = test(children.value)
         }else{
             print("children.label 为空")
         }
     }
     return keyValue
 }

上述代码中我们虽然完成了⼀个简单的 JSON 解析的Demo ,但是很多错误都是输出,如何在 Swift 中专业的表达错误那?

3. 错误处理 

Swift 提供 Error 协议来标识当前应用程序发生错误的情况,error 的定义如下:

 public protocol Error{ 
 }

所以不管是我们的 struct Class enum 我们都可以通过遵循这个协议来表示⼀个错误。这⾥我们选择 enum

 enum JSONMapError: Error { 
     case emptyKey 
     case notConformProtocol  
}

但是这⾥我们使⽤ return 关键字直接接收了⼀个 Any 的结果,如何抛出错误,正确的⽅式是使⽤ throw 关键字。 于此同时,编译器会告诉我们当前的我们的 function 并没有声明成 throws ,所以修改代码之后就能得出这样的结果了:

enum JSONMapError: Error { 
    case emptyKey 
    case notConformProtocol 
}

protocol CustomJSONMap { 
    func jsosnMap() thorws -> Any 
}

extension CustomJSONMap{
     func jsonMap() throws -> Any{
         let mirror = Mirror(reflecting: self)
         guard !mirror.children.isEmpty else {return self}
         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
     }
 }

我们来使⽤⼀下我们当前编写完成的代码,会发现编译器要求我们使⽤ try 关键字来处理错误。接下来我们就来说⼀说 Swift 中错误处理的⼏种⽅式:

  • 使⽤ try 关键字,是最简便的,也是我们最喜欢的:甩锅
  1. 使⽤ try 关键字有两个注意点:⼀个还是 try? ,⼀个是 try!
  2. try? :返回的是⼀个可选类型,这⾥的结果就是两类,⼀类是成功,返回具体的字典值;⼀类就错
    误,但是具体哪⼀类错误我们不关系,统⼀返回了⼀个nil
  3. try! 表示你对这段代码有绝对的⾃信,这⾏代码绝对不会发⽣错误!
     
  • 第⼆种⽅式就是使⽤ do...catch

如何你觉得仅仅使⽤ Error 并不能达到你想要详尽表达错误信息的⽅式,可以使⽤ LocalError 协议 , 定义如下:

public protocol LocalizedError : Error {
 /// A localized message describing what error occurred. 
 var errorDescription: String? { get }
 /// A localized message describing the reason for the failur e. 
 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 req uests help.
 var helpAnchor: String? { get }
}

CustomError 有三个默认属性:

  1. a static errorDomain
  2. an errorCode integer
  3. an errorUserInfo dictionary
     
 类似资料: