Swift 5.1 (16) - 錯誤處理

級別: ★☆☆☆☆
標籤:「iOS」「Swift 5.1 」「try?」「try!」「do-catch」
做者: 沐靈洛
審校: QiShare團隊php


錯誤的表示和拋出

在Swift中,錯誤由符合Error協議的類型的值表示。Error是空協議,表示類型可用於錯誤處理。git

錯誤的處理

Swift中的錯誤處理相似於其餘語言中的異常處理,使用了trycatchthrow關鍵字。可是與許多其餘語言(包括Objective-C)不同的是,Swift中的錯誤處理不涉及展現函數調用的堆棧信息。所以throwreturn語句的性能至關。github

使用拋出函數來傳播錯誤

拋出函數:函數聲明中標記有throws關鍵字。 在函數聲明的參數(...)後,返回箭頭->前使用throws關鍵字,表示此函數,方法或初始化方法能夠拋出錯誤。express

func throwError()throws -> String
func notThrowError() -> String
複製代碼

拋出函數能夠傳播錯誤,經過拋出其中的錯誤到函數被調用的範圍內。須要注意的是隻有拋出函數能夠傳播錯誤。在非拋出函數內的拋出的錯誤必須在函數內處理。 拋出函數示例:編程

enum VendingMachineError : Error {
    case InvalidGoods//!< 商品無效
    case StockInsufficient//!< 庫存不足
    case CoinInsufficient(coinNeeded:Int)
}

struct Item {
    var price : Int
    var count : Int
}

class VendingMachine {
    var coins : Int = 0
    var goods : [String : Item] = [
        "果粒橙": Item.init(price: 3, count: 2),
        "紅牛": Item.init(price: 5, count: 4),
        "雪碧": Item.init(price: 4, count: 6)]
    
    func sell(itemName name : String , itemCount count : Int) throws -> Void {
        
        guard let item = goods[name] else {
            throw VendingMachineError.InvalidGoods
        }
        guard item.count > 0 && item.count > count else {
            throw VendingMachineError.StockInsufficient
        }
        guard item.price * count <= coins else {
            throw VendingMachineError.CoinInsufficient(coinNeeded: item.price * count - coins)
        }
        //能夠成功購買
        coins -= item.price * count
        goods[name] = Item.init(price: item.price, count: item.count - count)
    }
}
複製代碼

使用try關鍵字調用拋出函數,以傳播錯誤。拋出函數傳播錯誤舉例:swift

class Customer {
    var itemName : String
    var itemCount : Int
    var vendingMachine : VendingMachine
    //使用`try`關鍵字調用拋出函數,以傳播錯誤
    //可拋出的初始化方法
    init(itemName:String,itemCount:Int,vendingMachine:VendingMachine) throws {
        try vendingMachine.sell(itemName: itemName, itemCount: itemCount)
        self.itemName = itemName
        self.itemCount = itemCount
        self.vendingMachine = vendingMachine
    }
    //可拋出函數
    func buy() throws -> Void {
        try vendingMachine.sell(itemName: itemName, itemCount: itemCount)
    }
}
複製代碼

使用do-catch處理錯誤

do-catch語句處理錯誤的形式:bash

do {
    try expression
    statements
} catch pattern 1 {
    statements
} catch pattern 2 where condition {
    statements
} catch {
    statements
}
複製代碼

進行錯誤處理的操做示例微信

class HandleError {
    class func test()->Void{
        let vendmachine = VendingMachine()
        vendmachine.coins = 10
        do {
            try vendmachine.sell(itemName: "紅牛", itemCount: 5)
            print("購買成功")
        } catch VendingMachineError.InvalidGoods {
            print("購買失敗" + "商品無效")
            
        } catch VendingMachineError.StockInsufficient {
            print("購買失敗" + "庫存不足")
            
        } catch VendingMachineError.CoinInsufficient(coinNeeded: let x){
            print("購買失敗" + "貨幣不足還需" + "\(x)個硬幣")
        }catch{
           print("購買失敗")
        }
    }  
}
複製代碼
class HandleError {
    class func test()->Void{
        let vendmachine = VendingMachine()
        vendmachine.coins = 10
        do {
            try vendmachine.sell(itemName: "紅牛", itemCount: 5)
            print("購買成功")
        } catch{
           print("購買失敗")
        }
    }
}
複製代碼

判斷捕獲的錯誤類型:函數

class HandleError {
    class func test()->Void{
        let vendmachine = VendingMachine()
        vendmachine.coins = 10
        do {
            try vendmachine.sell(itemName: "紅牛", itemCount: 5)
            print("購買成功")
        } catch is VendingMachineError {
           print("拋出的錯誤是VendingMachineError")
        } catch {
          print("拋出的錯誤不是VendingMachineError")
        }
    }
複製代碼

將錯誤轉換爲可選值

使用try?將錯誤轉換爲可選值來處理。若是在評估try?表達式時拋出了錯誤,則表達式的值爲nil源碼分析

func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction()
複製代碼

展開try?的寫法:

let y: Int?
do {
    y = try someThrowingFunction()
} catch {
    y = nil
}
複製代碼

禁止錯誤的傳播

使用try!來禁止錯誤的傳播,當所調用的拋出函數報錯時,將觸發運行時錯誤。

let x = try! someThrowingFunction()
複製代碼

指定清理操做

使用defer關鍵字能夠在當前範圍退出時,延遲執行指定的清理操做:好比關閉文件,清理內存等操做。無論是拋出錯誤仍是因爲返回或中斷等語句離開代碼塊,defer都將容許你執行其中的語句。 延遲操做語句:是由defer關鍵字和稍後要執行的語句組成的。該語句中不能包括控制轉移的語句,如:returnbreak;也不能拋出錯誤。 延遲操做語句的執行順序和其在代碼中寫入的位置是相反的,也就是說最後寫入的會首先被執行,依次類推。 延遲執行,在沒有錯誤須要處理的狀況下也可使用。

func processFile(filename: String) throws {
    if exists(filename) {
        let file = open(filename)
        defer {
            close(file)
        }
        while let line = try file.readline() {
        }
        // close(file) is called here, at the end of the scope.
    }
}
複製代碼

參考資料: swift 5.1官方編程指南


瞭解更多iOS及相關新技術,請關注咱們的公衆號:

image

小編微信:可加並拉入《QiShare技術交流羣》。

image

關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)

推薦文章:
淺談編譯過程
深刻理解HTTPS 淺談 GPU 及 「App渲染流程」
iOS 查看及導出項目運行日誌
Flutter Platform Channel 使用與源碼分析
開發沒切圖怎麼辦?矢量圖標(iconFont)上手指南
DarkMode、WKWebView、蘋果登陸是否必須適配?
奇舞團安卓團隊——aTaller
奇舞週刊

相關文章
相關標籤/搜索