美文网首页Swift
Swift 5.3 新特性精讲(2):多模式catch子句,不再

Swift 5.3 新特性精讲(2):多模式catch子句,不再

作者: 面试官小健 | 来源:发表于2020-07-08 22:04 被阅读0次

    在介绍多模式catch子句之前,我们先来复习一下平时是怎么catch的。

    单模式 catch 子句

    有以下函数:

    enum IOError: Error {
      case diskError(code:Int)
      case networkError(code:Int)
    }
    
    func foo(a: Int) throws -> Int {
      if a == 0 {
        return 0
      }
      throw IOError.networkError(code: 3)
    }
    

    在使用带有 throws 的函数的时候我们必须 try:除了 try?用于不成功变成空,try!用于不成功变崩溃(很少用)之外,最常用的就是try的语句了。

    一把梭catch:

    do {
      try print(foo(a: 1))
    } catch {
      print("error occurred")
    }
    

    一把梭catch,并进行变量绑定:

    do {
      try print(foo(a: 1))
    } catch let err {
      print("error occurred \(err)")
    }
    

    值得注意的是,如果当前的函数签名不带 throws, 则 catch 必须 exhaustive 穷举。但是你可能会问下面这个一把梭 catch 真的需要吗?函数foo不是只会抛出一种错误IOError吗?

    func myFunc() {
      do {
        try print(foo(a: 1))
      } catch let err as IOError {
        print("IO Error: \(err)")
      } catch {
        print("Unexpected Error")
      }
    }
    

    那么细的细节编译器可不知道,不写肯定是编译不过的,因为函数签名只约定了这个函数会不会 throws,并没有约定 throws 的具体类型是什么。尽管看起来粗糙,但实际对于二进制兼容是有帮助的。例如,增加、减少或者改变一种抛出的错误不会造成源代码兼容性被破坏。也可以用 is 来匹配:

    func myFunc() {
      do {
        try print(foo(a: 1))
      } catch is IOError {
        print("Caught IO Error")
      } catch {
        print("Unexpected Error")
      }
    }
    

    我们逐渐来到“模式匹配”(pattern matching)的领域,来看这个例子:

    func myFunc() {
      do {
        try print(foo(a: 1))
      } catch IOError.diskError(let code) {
        print("Caught error:\(code)")
      } catch IOError.networkError(let code) {
        print("Caught error:\(code)")
      } catch {
        print("Unexpected Error")
      }
    }
    

    这个例子展示了单模式子句的局限性。尽管绑定的是同一类型的 code,但是必须写成两个catch子句,并且重复处理逻辑。在 Swift 5.3 之前,要解决这个问题还得借助 switch,一个 case 语句可以多模式匹配,唯一的要求是绑定的值必须名字相同、类型相同、并且出现在每一个pattern中。

    func myFunc() {
      do {
        try print(foo(a: 1))
      } catch let err as IOError {
        switch err {
        case .diskError(let code), .networkError(let code):
          print("Caught error:\(code)")
        }
      } catch {
        print("Unexpected Error")
      }
    }
    
    

    不用我多说,这样的问题还是丑。

    多模式 catch

    有了多模式匹配后,明显漂亮了很多,我们不需要再借助 switch 写出多模式匹配。

    func myFunc() {
      do {
        try print(foo(a: 1))
      } catch IOError.diskError(let code), IOError.networkError(let code){
        print("Caught error:\(code)")
      } catch {
        print("Unexpected Error")
      }
    }
    

    为了展示多模式匹配的能力,我们玩得花一点:

    enum FooBarError: Error {
      case fooError(first:String, second:Int)
    }
    
    func myFunc() {
      do {
        try print(foo(a: 1))
      } catch IOError.diskError(let code) where code % 2 == 1, FooBarError.fooError(_, let code){
        print("Caught error:\(code)")
      } catch {
        print("Unexpected Error")
      }
    }
    

    这里加上了 where 子句用于筛选条件,并且同时匹配的 Error 换成了一个完全不同的类型,但是只要满足绑定的变量名字相同、类型相同、并且出现在每一个pattern中,那么就可以通过编译。

    不是有绑定才是模式匹配,用 is 的也可以两句并一句了。

    func myFunc() {
      do {
        try print(foo(a: 1))
      } catch is IOError, is FooBarError{
        print("Caught known error")
      } catch {
        print("Unexpected Error")
      }
    }
    

    但是两种 Error 类型能不能并一句呢?

    func myFunc() {
      do {
        try print(foo(a: 1))
      } catch let err as IOError, let err as FooBarError{
        print("Caught known error")
      } catch {
        print("Unexpected Error")
      }
    }
    

    上面的代码不能编译通过。尽管两个 err 名字相同,也都出现在两个pattern中,但是它们的类型不相同,违反了上面提到过的原则。

    小结

    通过多模式 catch 子句,我们可以像 switch 语句那样对于 Error 进行多模式匹配,减少重复的处理代码。

    多模式匹配时,需保证绑定的变量名字相同、类型相同并且出现在每一个模式中。

    相关文章

      网友评论

        本文标题:Swift 5.3 新特性精讲(2):多模式catch子句,不再

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