1. 程式人生 > >Swift:錯誤處理專題

Swift:錯誤處理專題

1. assert和precondition

//: Playground - noun: a place where people can play

import UIKit

// 下面的三個用於除錯,在真機上不起作用
assert(1>0) // 必須滿足括號裡面的邏輯,不然停止
assert(1>0, "Error") // 必須滿足括號裡面的邏輯,不然停止並報錯
assertionFailure("Error!") // 直接停止並報錯

// 下面的三個和assert作用一模一樣,在真機上也起作用
precondition(1>0)
precondition(1>0, "Error")
fatalError("Error")

2. throw,throws,系統自帶的協議Error

import UIKit

// 自動販賣機
class VendingMachine {

    struct Item {
        enum ItemType: String {
            case Water
            case Cola
            case Juice
        }

        let type: ItemType
        let price: Int
        var count: Int
    }

    // 繼承一個錯誤處理的類,名字叫Error
    enum ItemError: Error {
        case NoSuchItem
        case NotEnoughMoney(Int)
        case OutOfStock
        case OtherError
    }

    private var items = ["Mineral Water" : Item(type: .Water, price:2, count:10),
                         "Coca Cola" : Item(type: .Cola, price: 3, count: 5),
                         "Orange Juice" : Item(type: .Juice, price: 5, count: 3)]

    func vend(itemName: String, money: Int, count: Int) throws -> Int { // 如果有錯誤就丟擲,沒有就正常執行
        guard let item = items[itemName] else {
            throw ItemError.NoSuchItem // 程式結束,丟擲異常
        }
        guard money >= item.price else {
            throw ItemError.NotEnoughMoney(item.money) // 並攜帶一個返回值,是應付單價
        }
        guard item.count > 0 else {
            throw ItemError.OutOfStock
        }
        guard count > 0 else {
            throw ItemError.OtherError
        }
        dispenseItem(itemName: itemName)
        return money - item.price * item.count
    }

    private func dispenseItem(itemName: String) {
        items[itemName]?.count -= 1
        print("Enjoy your \(itemName)")
    }
}

3.try、do-catch、catch let ... as

對於一個這樣的函式

func vend(itemName itemName: String, money: Int) throws -> Int{
        
        guard let item = items[itemName] else{
            throw VendingMachine.ItemError.NoSuchItem
        }
        
        guard money >= item.price else{
            throw VendingMachine.ItemError.NotEnoughMoney(item.price)
        }
        
        guard item.count > 0 else{
            throw VendingMachine.ItemError.OutOfStock
        }
        
        dispenseItem(itemName: itemName)
        
        return money - item.price
    }

自定義的錯誤如下

enum ItemError: Error, CustomStringConvertible{         //swift 3
    case NoSuchItem
    case NotEnoughMoney(Int)
    case OutOfStock
    
    var description: String{
        switch self {
        case .NoSuchItem:                return "Not Such Item"
        case .NotEnoughMoney(let price): return "Not Enough Money. " + String(price) + " Yuan needed."
        case .OutOfStock:                return "Out of Stock"
        }
    }
}

現在我們處理異常的方式可以有如下幾種

Ps:可以丟擲異常的函式類似於可選性,不能直接呼叫,因此要麼用try,要麼用do-catch

<1> try! 表示強制解析,確認一定沒有異常,慎用!

pocketMoney = try! machine.vend(itemName: "Coca Cola", money: pocketMoney)

<2> try?表示如果沒有異常就正常進行,不然就返回nil

try? machine.vend(itemName: "Coca Cola", money: pocketMoney)

<3> do-catch,do裡面是正常邏輯,catch可以指定具體的錯誤型別,但是建議在最後加上一個catch什麼也不接的,這樣可以捕獲所有異常,catch中可以也可以用let捕獲錯誤裡帶有的值

do{
    pocketMoney = try machine.vend(itemName: "Coca Cola", money: pocketMoney)
    print(pocketMoney,"Yuan left")
}
catch VendingMachine.ItemError.NoSuchItem{
    print("No Such Item")
}
catch VendingMachine.ItemError.NotEnoughMoney(let price){ // 這裡可以獲取丟擲異常時自己帶有的值
    print("Not Enough Money." , price , "Yuan needed.")
}
catch VendingMachine.ItemError.OutOfStock{
    print("Out of Stock")
}
catch{
    print("Error occured during vending.")
}

<4> 如果想輸出error的具體資訊或者對error進行操作,也可以用catch let ... as語句,將錯誤轉成自己定義的型別,並進行一定的操作

do{
    pocketMoney = try machine.vend(itemName: "Coca Cola", money: pocketMoney)
    print(pocketMoney,"Yuan left")
}
catch let error as VendingMachine.ItemError{
    print(error)
}
catch{
    print("Error occured during vending.")
}

4. defer關鍵字

寫在函式裡,一個函式裡可以有多個,當且僅當函式退出作用域的時候執行,即正常return,或者丟擲異常。

如果一個函式有多個defer,只有退出作用域那行程式碼之前的defer才起作用,並且執行順序是倒序。

e.g.

func vend(itemName itemName: String, money: Int) throws -> Int{
    
    defer{
        print("Have a nice day")
    }
    
    guard let item = items[itemName] else{
        throw VendingMachine.ItemError.NoSuchItem
    }
    
    guard money >= item.price else{
        throw VendingMachine.ItemError.NotEnoughMoney(item.price)
    }
    
    guard item.count > 0 else{
        throw VendingMachine.ItemError.OutOfStock
    }
    
    defer{ // 如果丟擲異常了,這句話不會執行
        print("Thank you")
    }
    
    dispenseItem(itemName: itemName)
    
    return money - item.price
}