Swift - 異常處理


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 = nil
let tmpZ = try? testDefer(1)

 

 

 

 

 輸出:
testDefer begin
testDefer exit

 

 

沒有 defer 拋出異常的執行邏輯

func testDeferNormal() {
    print("testDefer begin")
    defer {
        print("testDefer exit")
    }

    print("testDefer end")
}
testDeferNormal()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM