在介紹多模式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 進行多模式匹配,減小重複的處理代碼。
多模式匹配時,需保證綁定的變量名字相同、類型相同而且出如今每個模式中。
掃碼下方二維碼關注「面試官小健」