Error Handling

作者: 宋奕Ekis | 来源:发表于2021-08-18 12:50 被阅读0次

    Representing and Throwing Errors

    We can use enumeration to conform Error protocol to implement a customer error, just like the following code,

    enum VendingMachineError: Error {
        case invalidSelection
        case insufficientFunds(coinsNeeded: Int)
        case outOfStock
    }
    

    Handling Errors

    Propagating Errors Using Throwing Functions

    Function with throws keyword before return type can be referred as a throwing function, which can propagate its inner errors out.

    func canThrowErrors() throws -> String
    
    struct Item {
        var price: Int
        var count: Int
    }
    
    class VendingMachine {
        var inventory = [
            "Candy Bar": Item(price: 12, count: 7),
            "Chips": Item(price: 10, count: 4),
            "Pretzels": Item(price: 7, count: 11)
        ]
        var coinsDeposited = 0
    
        func vend(itemNamed name: String) throws {
            guard let item = inventory[name] else {
                throw VendingMachineError.invalidSelection
            }
    
            guard item.count > 0 else {
                throw VendingMachineError.outOfStock
            }
    
            guard item.price <= coinsDeposited else {
                throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
            }
    
            coinsDeposited -= item.price
    
            var newItem = item
            newItem.count -= 1
            inventory[name] = newItem
    
            print("Dispensing \(name)")
        }
    }
    

    We throw an error within function means we exit the function.

    And we can use try keyword to handle the error when we call throwing function, and we can propagate the error continually if the call-function is too throwing function.

    let favoriteSnacks = [
        "Alice": "Chips",
        "Bob": "Licorice",
        "Eve": "Pretzels",
    ]
    func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
        let snackName = favoriteSnacks[person] ?? "Candy Bar"
        try vendingMachine.vend(itemNamed: snackName)
    }
    

    Even in an initializer function,

    struct PurchasedSnack {
        let name: String
        init(name: String, vendingMachine: VendingMachine) throws {
            try vendingMachine.vend(itemNamed: name)
            self.name = name
        }
    }
    

    Handling Errors Using Do-Catch

    And also, as the most familiar way, we can use Do-Catch to handle the error.

    do {
        try expression
        statements
    } catch pattern 1 {
        statements
    } catch pattern 2 where condition {
        statements
    } catch pattern 3, pattern 4 where condition {
        statements
    } catch {
        statements
    }
    

    We can some examples below:

    var vendingMachine = VendingMachine()
    vendingMachine.coinsDeposited = 8
    do {
        try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
        print("Success! Yum.")
    } catch VendingMachineError.invalidSelection {
        print("Invalid Selection.")
    } catch VendingMachineError.outOfStock {
        print("Out of Stock.")
    } catch VendingMachineError.insufficientFunds(let coinsNeeded) {
        print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
    } catch {
        print("Unexpected error: \(error).")
    }
    // Prints "Insufficient funds. Please insert an additional 2 coins."
    
    func nourish(with item: String) throws {
        do {
            try vendingMachine.vend(itemNamed: item)
        } catch is VendingMachineError {
            print("Couldn't buy that from the vending machine.")
        }
    }
    
    do {
        try nourish(with: "Beet-Flavored Chips")
    } catch {
        print("Unexpected non-vending-machine-related error: \(error)")
    }
    // Prints "Couldn't buy that from the vending machine."
    
    func eat(item: String) throws {
        do {
            try vendingMachine.vend(itemNamed: item)
        } catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock {
            print("Invalid selection, out of stock, or not enough money.")
        }
    }
    

    Converting Errors to Optional Values

    If a throwing function do have return values, but the error throwing forbid the function, so the return values will because optional values.

    We can use try? to handle there values.

    func fetchData() -> Data? {
        if let data = try? fetchDataFromDisk() { return data }
        if let data = try? fetchDataFromServer() { return data }
        return nil
    }
    

    Specifying Cleanup Actions

    The defer keyword is very useful, we can handle all the circumstances we want to do when function exit.

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

    And, it conply the FIFO rules, if we have a number of defer expression, the first will be called at last, and the last one will be called first.

    Let’s think!

    相关文章

      网友评论

        本文标题:Error Handling

        本文链接:https://www.haomeiwen.com/subject/yjjgbltx.html