總會報錯:異常處理

這節課我們一起來認識一下 Swift 中的錯誤處理

在調用方法和寫一個輪子的時候,總會有各種各樣奇奇怪怪的錯誤,就是已經正常編譯的軟件,也會出現一些不可預期的錯誤。不過,這些錯誤當中,有一些是可以被識別和捕捉的——它們可預期。

可預期的錯誤

為什麼我們說有一些錯誤是可以預料得到的呢?比如說讀取一個文件的時候文件不存在、保存一個文檔的時候目錄不可寫、下載文件的時候網絡無連接、傳送一個參數的時候範圍超出控制……這些錯誤都是可以預料得到的!我們完全沒有必要讓程序在這些錯誤裡崩潰!

那麼,在這些情況下我們就需要一個完善的異常處理機制,把這些可以預料到的異常都給捕捉起來,用友好的方式處理掉,避免我們的程序栽在這些已知的錯誤上。

拋出錯誤和抓住錯誤

我們調用了一個方法,如果必要,那你必須要明白這個方法是有風險的,它有可能會執行失敗——這個時候它就會將失敗的原因拋出來——拋出錯誤。

你必須將它可能拋出錯誤的情況都考慮好,如果這樣報錯,怎麼辦,如果那樣報錯,又怎麼辦!

所以,如果我們創建的方法可能有風險,就一定要用 throws 標記出來,這樣調用的時候我們就可以用 catch 來接住這個炸彈。

創建一個包含錯誤狀態的枚舉

我們用枚舉類型來創建可能的錯誤狀況,只需要讓枚舉遵循 ErrorType 協議即可,其他的都交給編譯器完成。

比如說我們這裡寫兩個錯誤,一個是槍管過熱,另一個是沒有子彈了!

有風險的方法

然後我們再來改造 Gun 的 fire() 方法:

一個簡單的模型,每次發射之前檢查彈藥數量,檢查槍管溫度,如果都 OK,就射擊,同時槍管溫度增加,彈藥量減少。(這裡溫度設置不一定合常理哈)

處理錯誤

然後我們來調用這個方法11次看看會有什麼樣的情況發生:

最終,我們的到的結果是:

Guard 是 if 嗎?

答:不是。

如你所見,Guard 語法用起來好像很像是 if,但它不是,你可以理解為它是個反的 if , 如果我們反過來這樣寫也是完全可以的:

看起來是不是很繞?一堆一堆的大括號看著就眼暈!

使用 Guard 則就好像是個守門員——我們叫他“守門模式”,其實看起來就好像是反過來用 if:

這樣雖然一樣了,你注意到沒有?我們把大於小於號給翻轉了!因為這樣才能讓 if 起到我們想要的作用,或者,我們可以這樣用?

這樣大小的邏輯是擺正了,可是多出來的嘆號更讓人頭大了,一不小心還容易擾不過這個彎兒……

所以,我們用 Guard,它也判斷是非,如果“是”則不作用,如果“非”,就執行 else 裡的內容,這樣行內的邏輯對人來說就很直觀了——不再需要曲線救國。

有風險的方法要放在 do – catch 塊裡

就像我前面的例子,我們把 fire 方法改造成能夠拋出異常的方法,那麼調用它的時候就必須把它放進特製的容器當中,這樣編譯器才能搞定和理解那些錯誤。

如上,我們在 do 的代碼塊當中來嘗試方法(try),如果正常那皆大歡喜。如果方法拋出了異常,那我們就會執行到 catch 上——這裡其實看起來好像 if else 的嵌套,也好像 switch 的選擇——不過,catch 確實要求把可能會拋出的錯誤都寫出來如果你不這麼做,那麼這個炸彈落地你的程序也就會被炸掉啦!

不過,就像 switch 一樣,你也可以只寫 catch 而不跟錯誤類型,這樣就可以默認匹配所有錯誤了——缺點是你也不能知道發生了什麼錯誤。

與調用者溝通

那我們做這個複雜的動作究竟有什麼意義呢?直接把這些寫成一個狀態不更好?

還真不會更好。因為我們的方法並不一定就是給自己用,一個程序也不會只有一個人完成,當別人調用你的方法的時候,難道非得讓人家先讀一下800字的文檔?在需要的位置拋出一個錯誤,寫清楚錯誤的類型——調用者就可以方便地處理掉這個問題。

傳球!

我們說遇到風險就要 try,而 try 出來的錯誤要 catch!可是沒有 catch 住的炸彈怎麼辦?如果你不想拆彈呢?其實也可以,如果你在你的方法裡調用了一個有風險的方法,而你又不想在這個方法里拆彈,那就不要管它就好了——當然,要記得把你的方法也標記為 throws ,這樣這個炸彈會繼續往下傳!

呃,但你得記住,最終得有個方法來 catch 和拆彈,不然,你的程序還是會被炸掉。

如果一個問題得不到解決,它不會自行消失。

對了,如果你不想讓錯誤拋出,那可以強制 try,使用關鍵字 try! ,這樣就不需要拆彈了……因為遇到炸彈不用扔,直接炸。

本文由 落格博客 原創撰寫:落格博客 » 總會報錯:異常處理

轉載請保留出處和原文鏈接:https://www.logcg.com/archives/1137.html

關於作者

R0uter

如非聲明,本人所著文章均為原創手打,轉載請註明本頁面鏈接和我的名字。

發表評論

您的電子郵件地址不會被公開. 必填字段標 *