標籤:就會 develop nbsp defer cti 添加 崩潰 hand 蘋果
Swift 對 Error Handling詳解
蘋果官方文檔解釋
跟其它語言一樣,Swift的異常處理是在程式拋出異常後的處理邏輯。 Swift提供了一流的異常拋出、捕獲和處理的能力。跟Java語言類似, Swift的異常並不是真正的程式崩潰, 而是程式啟動並執行一個邏輯分支;Swift和Java捕獲異常的時序也是一樣的。當Swift運行時拋出異常後並沒有被處理, 那麼程式就會崩潰。
在Swift語言中使用Error表示異常, 作用同Java的Exception類或Object-C的NSError類。 蘋果建議使用枚舉作為異常類型(為什麼不推薦用類或者結構體?答案是列舉資料型別本身就是分成若干種情況,很適合做邏輯分支判斷條件)。
enum VendingMathineError: Error { case invalidSelection case insufficientFunds(coinsNeed: Int) case outOfStack}
上面聲明了枚舉類型VendingMathineError,繼承於Error。 注意Swift的所有異常類型都繼承於Error, 就像Java所有異常類都繼承於Exception一樣。
類似於Java處理異常的try/catch/finally, Swift提供了try、try?、try!、catch、throw、throws關鍵字處理異常邏輯,用法跟Java也很像。
如何聲明一個可能拋出異常的函數? 在函數參數括弧後面添加throws關鍵字, 跟Java文法有點像;區別是Swift的throws後面不用跟著異常類、而Java的throws後面要有異常類名稱。 你只要告訴Swift這個函數可能拋出異常就夠了,不需要說明到底是哪種異常,函數體內可以拋出任意類型的異常(肯定是繼承於Error)。
func canThrowErrors() throws -> String
func canThrowErrors(type: Int) throws -> String? { //函數體寫成switch/case更好一些 if type == 1 { throw VendingMathineError.invalidSelection } if type == 2 { throw VendingMathineError.outOfStack } if type == 3 { throw VendingMathineError.insufficientFunds(coinsNeed: 100) } return "success"}
上面測試代碼是為了測試拋異常邏輯, 函數體寫成switch/case更好一些。 從canThrowErrors函數看出,當參數為1、2或3時會拋異常, 文法是throw ... 且程式會跳出函數體,文法同Java。
Swift提供了一種類似於Java try/catch的文法, 是do(函數體內必須有try且該語句可能拋出異常)、catch。
do {try expression statements} catch pattern 1 { statements} catch pattern 2 where condition { statements}
注意:如果try語句拋出異常則會跳出do代碼塊,並按順序逐個catch,當一個catch捕獲成功後,後面的catch不再執行。
do { var data = try canThrowErrors(type: 3)//執行這個函數 這個函數可能拋出異常 print("after execute canThrowErrors") if data != nil { print("Error test data:\(data)") }} catch VendingMathineError.outOfStack { print("outOfStack")} catch VendingMathineError.invalidSelection { print("invalidSelection")} catch { //類似於Java的catch(Exception ex) print("Error")}
輸出:Error
try canThrowsErrors(type: 3)會拋出VendingMathineError.isSufficientFunds(coinsNeed:100),不屬於前2個catch類型, 而最後一個catch是捕獲所有異常, 所有會執行其函數體。
是不是感覺少了點什嗎? 對, 少了個類似於Java的finally,後面會介紹。
下面再介紹一下try?和try!的用法。
let x = try? someThrowingFunction()//與下面的相同let y: Int? do{ y = try someThrowingFunction()}catch { y = nil}
try?後面的語句可能會拋出異常, 如果拋出異常則賦值nil給左側;如果沒拋出異常則將傳回值賦給左側;
try!取消異常捕獲邏輯,文法有點任性,相當於裸奔, 明知可能拋出異常,但自信這段代碼不會拋異常。 try!是try?的補充。你確定後面語句不會拋出異常,但真的拋出異常後程式會崩潰。不建議使用try!,有使用情境推薦使用try?
let tmpX = try? canThrowErrors(type: 1) //如果拋出異常程式正常運行並賦值nil給左側, 如果沒拋異常則將傳回值賦給左側//let tmpY = try! canThrowErrors(type: 2) //你很確定不會拋出異常時這樣用,但如果運行時拋異常會導致程式崩潰
Swift使用defer關鍵字作用同Java的finally, 即使代碼塊內有break、continue、return或者拋異常,在結束代碼塊後仍然會執行defer代碼塊
下面代碼只是為了測試,驗證函式體內拋出異常時的執行時序, 文法邏輯跟finally一模一樣。
func testDefer(_ param: Int) throws -> String { print("testDefer begin")
defer {//拋出異常就執行defer 為拋出異常就最後執行defer print("testDefer exit") }
// do something...
if param == 1 { throw VendingMathineError.invalidSelection } print("testDefer end")return "testDefer return" }//調用函數 該函數拋出異常tmpZ = nillet tmpZ = try? testDefer(1)
輸出:testDefer begintestDefer exit
沒有 defer 拋出異常的執行邏輯
func testDeferNormal() { print("testDefer begin") defer { print("testDefer exit") } print("testDefer end")}testDeferNormal()
swift - 異常處理