Swift 中的錯誤與異常

一銘發表於2017-12-13

推薦推薦推薦

本文為學習筆記,對錯誤和異常的處理在我個人的程式碼中做的比較爛...而 Swift中有非常有些的處理錯誤機制,故將自己的學習記錄成文.

###反慣例,先上參考文章: ####1.喵神的 Swifter必備 tips 第三版 ####2.Objc.cn ####3.Swift 進階(已更新到 Swift 3.0)

###1.什麼是異常和錯誤? 異常存在在我們每天的編碼中,所有導致程式無法執行的問題都可以稱之為異常.比如向一個無法響應的物件傳送了訊息或者訪問超過陣列元素下標都會產生異常,從而導致APP crash. 而錯誤是可能存在的合理的問題,比如登入錯誤或者一些 api 的 error,但是在 oc 中遇到傳入 NSErrorPointer 指標去儲存錯誤資訊,但是我個人都是一般都是傳個 nil....

###2.Swift帶來了什麼新變化? 在 Swift2.0之後,這門語言引入了異常機制,帶有 NSError的 API變成了可以丟擲異常的API.

public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
複製程式碼

含有 throws 的方法會丟擲一個異常,而 rethrows和 throws 做的是一樣的事情, rethrows 一般是用在引數中含有 throws 的方法中,如上, map函式可以接受一個 transform 函式,也可以一個能丟擲異常的函式作為引數.

我來貼下我改造過的登入功能: ####1.先定義一個用來表示錯誤型別的 Enum, 它遵從 Error protocol(Swift3)(ErrorType Swift2),它是一個空的 protocol 的,它只用來標示這個型別是個錯誤型別.

enum LoginError: Error {
    case ErrorPassword(Int) //密碼錯誤
    case UserNotFound //賬號錯誤
}
複製程式碼

####2.再開始寫登入方法,這是一個可以丟擲異常的方法,所以要把 throws 寫在函式宣告以後:

func login(user: String, password: String) throws {
    if user != "admin" {
        throw LoginError.UserNotFound // 丟擲異常
    }
    if password != "password" {
        throw LoginError.ErrorPassword(404) // 丟擲異常
    }
    print("login success") // 登入成功
}
複製程式碼

####3.在呼叫會丟擲異常的方法前面使用 try 關鍵字;
如果要捕獲丟擲的異常,就需要吧會丟擲異常額程式碼放在 do 包含的程式碼塊裡;
在使用 catch 關鍵字來匹配各種需要捕獲的異常.

do {
    try login(user: "admina", password: "password")
} catch LoginError.UserNotFound {
    print("UserNotFound")
} catch let LoginError.ErrorPassword(errorCode) {
    print("ErrorPassword")
    print(errorCode)
}

//log: UserNotFound
複製程式碼

如果在確定這個函式呼叫不會發生異常,我們可以使用 try! 來呼叫:

try! login(user: "admina", password: "password")
複製程式碼

這樣來呼叫就不用把程式碼塊放在 do catch 的程式碼塊中,但是這樣寫一旦這個呼叫丟擲異常,Swift 就會引發執行時異常. ###3.錯誤型別結合泛型 還是使用登入模組,如果把得到的結果和錯誤異常都看作是一個單獨的型別,然後把成功和錯誤看成了兩種不同的情況,那麼:

enum LoginResult<T> {
    case success(T)
    case failure(String)
}
複製程式碼

使用一個泛型的 enum 來表示返回的結果,當呼叫成功時,就返回. success 中的 T型別,如果失敗,則返回. failure 中的 String,返回失敗的原因.

func userLogin<T>(user: String, password: String) -> LoginResult<T> {
    if user != "admin" {
        return LoginResult<T>.failure("error userName")
    }
    if password != "password" {
        return LoginResult<T>.failure("error password")
    }
    guard let value = "login success" as? T else {
        return LoginResult<T>.failure("some error")
    }
    return LoginResult<T>.success(value)
}
複製程式碼

那麼登入的函式就可以返回一個帶泛型的 enum, 可以是返回結果統一化.

let result: LoginResult<String> = userLogin(user: "admin", password: "password")

switch result {
case .success(let result):
    print(result)
case .failure(let errorMessage):
    print(errorMessage)
}
複製程式碼

###4.總結 在 Swift 的程式設計中,因為 Swift 的獨特性,最好不要忽視錯誤型別.靈活使用 throws 和結合泛型能讓我們的程式碼即優雅又安全. //:TO DO(xxx)

相關文章