Viblo Learning
+5

Error Handling trong Swift

Khái niệm

Error handling is the process of responding to and recovering from error conditions in your program. Swift provides first-class support for throwing, catching, propagating, and manipulating recoverable errors at runtime.

Tạm dịch là: Error Handling là tiến trình phản hồi và khôi phục từ các điều kiện lỗi trong chương trình của bạn. Swift cung cấp first-class để hỗ trợ throwing, catching, propagating và manipulating recoverable lỗi trong thời gian chạy.

Một vài hoạt động không được đảm bảo rằng luôn luôn thành công hoặc không cho ra được kết quả hợp lý. Optionals được sử dụng để thể hiện việc biến không có giá trị, nhưng khi một số xử lý thất bại, nó cần phải biết nguyên nhân thất bại là gì, để code của bạn có thể có những xử lý tiếp theo cho phù hợp. Như ví dụ, xem xét nhiệm vụ đọc và xử lý dữ liệu từ tập tin trên ổ đĩa. Nhiệm vụ này có thể bị lỗi, như tập tin không tồn tại, tập tin không có quyền đọc hoặc tập tin không có định dạng phù hợp. Theo những trường hợp khác, cho phép một chương trình giải quyết lỗi và thông báo với người dùng bất kỳ lỗi nào mà nó có thể giải quyết.

Representing and throwing error

Trong Swift, lỗi được đại diện bởi giá trị của những kiểu tuân theo protocol ErrorType. Protocol này cho thấy rằng một kiểu có thể được sử dụng để xử lý lỗi. Những kiểu liệt kê (Enum) trong Swift phù hợp nhất để mô hình một nhóm liên quan điều kiện lỗi, giá trị được liên kết cho phép bổ sung thông tin chi tiết của một lỗi. Ví dụ đây là 1 cách xử lý lỗi trong game:

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

Throw lỗi cho phép bạn chỉ ra rằng có điều gì đó hoạt động sai đã xảy ra và tiến trình đang thực hiện không thể tiếp tục. Bạn sử dụng throw để thông báo 1 lỗi đã xảy ra. Ví dụ, mã sau đây phát ra một lỗi để cho biết rằng năm đồng tiền bổ sung là cần thiết của máy bán hàng tự động:

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

Hadling errors

Khi một lỗi phát sinh, một vài phần code ở đó phải chịu trách nhiệm xử lý lỗi. Ví dụ: Để xử lý vấn đề cần thử một phương pháp thay thế, hoặc thông báo cho người dùng về việc đã xảy ra lỗi.

Có 4 cách để xử lý lỗi trong Swift. Bạn có thể thông báo lỗi đến hàm đã gọi hàm đó bằng cách xử dụng lệnh do-catch, xử lý lỗi là 1 giá trị tuỳ chọn: có thể xảy ra lỗi hoặc không.

Khi một hàm ném lỗi, nó sẽ thay đổi luồng code của bạn, vì vậy điều quan trọng là bạn có thể nhanh chóng xác định vị trí trong code của mình có thể gây ra lỗi. Để xác định những địa điểm này trong mã của bạn, hãy viết thử từ try? hoặc try! biến thể-trước một đoạn mã gọi một function, method, hoặc initializer có thể phát sinh một lỗi.

Propagating error using throw function

Để thấy rằng một function, method, hoặc initializer có thể phát sinh lỗi, bạn viết một từ khóa throws để gọi một throwing function. Nếu hàm chỉ định một kiểu trả về, bạn viết từ khóa throws trước khi cho phép trở về (->).

func canThrowErrors() throws -> String
 
func cannotThrowErrors() -> String

Một throwing function được throw bên trong của nó và báo lỗi về phần code gọi đến nó.

Dưới đây là ví dụ về lỗi của VendingMachine:

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)")
    }
}

Việc thực hiện hàm vend(itemNamed 😃 sử dụng các câu lệnh guard để thoát ra khỏi phương thức sớm và đưa ra các lỗi thích hợp nếu không đáp ứng bất cứ yêu cầu nào đối với việc snack. Bởi vì lệnh throw ngay lập tức điều hướng lỗi của chương trình, một mặt hàng (kết quả trả về) sẽ được cung cấp chỉ khi tất cả các yêu cầu này được đáp ứng (phải vượt qua tất cả lệnh guard).

Bởi vì phương thức vend(itemNamed:) có thể trả về lỗi, nên bất kỳ mã nào gọi phương thức này đều phải xử lý các lỗi bằng cách sử dụng lệnh do-catch, try?, hoặc try! - hoặc tiếp tục truyền chúng đi tới những đoạn code tiếp theo. Ví dụ, buyFavoriteSnack(person:vendingMachine:) trong ví dụ dưới đây cũng là một throwing function, và bất kỳ lỗi nào mà vend(itemNamed:) hàm này sẽ truyền throws tiếp đến nơi mà hàm buyFavoriteSnack(person:vendingMachine:) được gọi.

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)
}

Trong ví dụ này, hàm buyFavoriteSnack(person: String, vendingMachine: VendingMachine) trả về món ăn yêu thích và cố gắng mua nó cho họ bằng cách gọi phương thức vendingMachine.vend(itemNamed: snackName). Bởi vì phương thức vend(itemNamed:) có thể đưa ra lỗi, nên nó được gọi với từ khoá try ở phía trước.

Handling Errors Using Do-Catch

Bạn sử dụng lệnh do-catch để xử lý các lỗi khi chạy một đoạn code. Nếu một lỗi được throw ra bởi mã trong mệnh đề do, nó được chuyển sang hàm catch để xác định một trong số chúng có thể xử lý lỗi. Đây là 1 ví dụ điển hình:

Converting Errors to Optional Values

Bạn có thể sử dụng try? để xử lý một lỗi bằng cách chuyển nó sang một giá trị Optional. Nếu một lỗi được ném ra trong khi try? biểu hiện, giá trị của biểu thức là nil. Ví dụ, trong mã sau xy có cùng một giá trị và quan hệ:

func someThrowingFunction() throws -> Int {
    // ...
}
 
let x = try? someThrowingFunction()
 
let y: Int?
do {
    y = try someThrowingFunction()
} catch {
    y = nil
}

Disabling Error Propagation

Trong trường hợp bạn biết chắc chắn không xảy ra lỗi bạn có thể dùng try! trước đó. Ví dụ, đoạn code sau đây sử dụng một hàm loadImage(atPath:), hàm đó để load ảnh từ resources từ 1 đường dẫn hoặc trả về 1 lỗi nếu không load được hình ảnh. Trong trường hình ảnh chắc chắn tồn tại và không có lỗi xảy ra, bạn có thể tắt throw error như sau:

let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")

Bài viết dịch từ The Swift Programming Language (Swift 4.0.3)


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.