iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 26
0

錯誤處理

錯誤處理是對程序中的錯誤條件進行回應及恢復的過程, Swift 在運行時為拋出、捕獲、傳播可恢復的錯誤提供了一流的輔助。

某些操作不能保證總是完整的執行或是產生有用的輸出,可選項用於表示沒有值,但是操作失敗了,了解導致失敗的原因通常是很有用的,以便你的程式碼能做出相對應的回應。


表示和拋出錯誤

在 Swift 中,錯誤由符合錯誤協議的類型值表示,這個空的協議表示一個類型可以用於錯誤處理。Swift 中的枚舉特別適合對一組相關的錯誤條件進行建模,其中的相關值允許傳遞有關錯誤性質的附加訊息:

enum MovieRule:Error {
    case age
    case money
    case food
}

拋出一個錯誤可以讓你指出意外事件的發生,正常的執行流程不能繼續。你可以用一個 throw 語句來拋出一個錯誤。

throw MovieRule.age //年齡限制
throw MovieRule.money //金錢不足

處理錯誤

當發生一個錯誤時,一些程式碼必須負責處理錯誤。在 Swift 中有四種方法可以處理錯誤。你可以從函數傳播到調用該函數的程式碼,使用 do-catch 語句處理錯誤,把錯誤作為可選項的值,或者錯誤不會發生的斷言。

當一個函數拋出一個錯誤時,他會改變你的程式的流程,所以重要的是你可以在程式碼中快速識別可能拋出錯誤的地方。要在你的程式碼中標示這些地方,請編寫 try 關鍵字 、 try? 或 try! 變體,放在調用函數、方法或者會拋出錯誤的初始化器程式碼之前。


使用拋出函數傳遞錯誤

為了表示一個函數或者方法可以拋出錯誤,你要在它的宣告當中的參數後邊寫上 throws 關鍵字。使用 throws 標記的函數叫做拋出函數。如果函數指定了返回類型,則在返回箭頭( -> )之前寫入throws關鍵字。例如:

func canThrowErrors() throws -> String // 拋出錯誤
func cannotThrowErrors() -> String // 無法拋出

拋出函數可以把它內部拋出的錯誤傳遞到它被調用的生效範圍之內。
只有拋出函數可以傳遞錯誤。任何在非拋出函數中拋出的錯誤都必須在該函數內部處理。

我們下面用上面的 MovieRule 來做一個看電影的限制,我們希望高於 18 歲的人才能觀賞電影,並且錢必須超過 200,如果有攜帶食物則無法入場,如果這些規則都通過了,則回傳一個 "符合入場資格" 的訊息:

func seeMovie(age:Int,money:Int,food:Bool) throws -> String{
    guard age > 18 else {
        throw MovieRule.age
    }
    guard money >= 200 else {
        throw MovieRule.money
    }
    guard food == false else {
        throw MovieRule.food
    }
    
    return "符合入場資格"
}

當你在測試錯誤方法的時候,必須使用 try 關鍵字來調用它:

try seeMovie(age: 20, money: 300, food: false)

結果如下:
https://ithelp.ithome.com.tw/upload/images/20180114/20107701n6vIBK2sW4.png

如果有其中一項錯誤拋出,則不會顯示任何訊息:
https://ithelp.ithome.com.tw/upload/images/20180114/20107701T1y6mEay0n.png


使用Do-Catch 處理錯誤

我們利用上述的範例加上 Do-Catch 來處理:

do{
    try seeMovie(age: 20, money: 300, food: false)
    try seeMovie(age: 23, money: 300, food: false)
    print("符合觀賞資格")
}
catch{
    print("很抱歉,沒有入場資格")
}

我們這個 GroupMovie() 就是用來查看裡面的這些是否都符合情況,如果符合則輸出 do{} 中的符合觀賞資格,若不符合則印出 catch{} 中的沒有入場資格:

https://ithelp.ithome.com.tw/upload/images/20180114/201077019jPTA7IVw1.png

只要有其中一項不符合規則,那就會失去入場資格,例如他帶了食物去看電影(food = true):
https://ithelp.ithome.com.tw/upload/images/20180114/20107701moQdCQO2kV.png

當然我們也能根據他的錯誤內容來做出相對的回應,只要在 catch 後面加上 MoiveRule 的錯誤規則,就能根據不同情況印出不同的訊息:

do{
    try seeMovie(age: 20, money: 300, food: false)
    try seeMovie(age: 23, money: 300, food: false)
    print("符合觀賞資格")
}
catch MovieRule.age {
    print("很抱歉,有人未滿18歲")
}
catch MovieRule.food {
    print("抱歉禁止攜帶食物")
}
catch MovieRule.money{
    print("金錢不足")
}

因此假如我們這時其中一個人禁止攜帶外食為 true ,那麼就能印出相對應的錯誤訊息:

https://ithelp.ithome.com.tw/upload/images/20180114/20107701RfjPFmTJvv.png


轉換錯誤為可選項

使用 try? 通過將錯誤轉換為可選項來處理一個錯誤。如果一個錯誤在 try? 表達式中拋出,則表達式的值為 nil ,如此一來我們也能透過此方式來檢查上面的範例,如果不符合規則那麼則反為 nil ,再加上一個 if 判斷式來檢查是否為 nil :

if(try? seeMovie(age: 20, money: 300, food: true)) == nil{
        print("不符合入場資格")
    } else {
        print("符合入場資格")
    }

結果為下:
https://ithelp.ithome.com.tw/upload/images/20180114/20107701DqOGEf1G5t.png


取消錯誤傳遞

事實上有時你已經知道一個拋出錯誤或者方法不會在運行時拋出錯誤。在這種情況下,你可以在表達式前寫 try! 來取消錯誤傳遞並且把調用放進不會有錯誤拋出的運行時斷言當中。如果錯誤真的拋出了,你會得到一個運行時錯誤。



上一篇
Day-25 Swift 語法(21) - 鏈的多層連接
下一篇
Day-27 Swift 語法(23) - 類型轉換
系列文
Swift 菜鳥的30天30

尚未有邦友留言

立即登入留言