在介绍多模式catch子句以前,咱们先来复习一下平时是怎么catch的。面试
有如下函数:swift
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:ui
do {
try print(foo(a: 1))
} catch {
print("error occurred")
}
复制代码
一把梭catch,并进行变量绑定:spa
do {
try print(foo(a: 1))
} catch let err {
print("error occurred \(err)")
}
复制代码
值得注意的是,若是当前的函数签名不带 throws
, 则 catch 必须 exhaustive 穷举。可是你可能会问下面这个一把梭 catch 真的须要吗?函数foo
不是只会抛出一种错误IOError
吗?.net
func myFunc() {
do {
try print(foo(a: 1))
} catch let err as IOError {
print("IO Error: \(err)")
} catch {
print("Unexpected Error")
}
}
复制代码
那么细的细节编译器可不知道,不写确定是编译不过的,由于函数签名只约定了这个函数会不会 throws,并无约定 throws 的具体类型是什么。尽管看起来粗糙,但实际对于二进制兼容是有帮助的。例如,增长、减小或者改变一种抛出的错误不会形成源代码兼容性被破坏。也能够用 is
来匹配:code
func myFunc() {
do {
try print(foo(a: 1))
} catch is IOError {
print("Caught IO Error")
} catch {
print("Unexpected Error")
}
}
复制代码
咱们逐渐来到“模式匹配”(pattern matching)的领域,来看这个例子:cdn
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")
}
}
复制代码
不用我多说,这样的问题仍是丑。string
有了多模式匹配后,明显漂亮了不少,咱们不须要再借助 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 进行多模式匹配,减小重复的处理代码。
多模式匹配时,需保证绑定的变量名字相同、类型相同而且出如今每个模式中。
扫码下方二维码关注“面试官小健”