错误

温馨提示:本文最后更新于2024-07-31 18:54:37,某些文章具有时效性,若有错误或已失效,请在下方留言

错误基础知识

所有要抛出的错误都必须是符合Error协议的枚举。

enum PasswordError: Error {
    case empty
    case short
}

错误可以添加关联值

enum PasswordError: Error {
    case empty
    case short
    case obvious(message: String)
}

将函数或方法标记为可能抛出错误,请在其返回类型前添加 throws

func encrypt(_ str: String, with password: String) throws -> String {
    // complicated encryption goes here
    let encrypted = password + str + password
    return String(encrypted.reversed())
}

然后,使用dotrycatch的组合来运行有风险的代码。

do {
    let encrypted = try encrypt("secret information!", with: "T4ylorSw1ft")
    print(encrypted)
} catch PasswordError.empty {
    print("You must provide a password.")
} catch PasswordError.short {
    print("Your password is too short.")
} catch PasswordError.obvious {
    print("Your password is obvious")
} catch {
    print("Error")
}

要使用关联值,需要将其绑定到catch代码块中的常量:

catch PasswordError.obvious(let message) {
    print("Your password is obvious: \(message)")
}

在处理关联值时,还可以使用模式匹配。

enum PasswordError: Error {
    case empty
    case short(minChars: Int)
    case obvious(message: String)
}

catch PasswordError.short(let minChars) where minChars < 5 {
    print("We have a lax security policy: passwords must be at least \(minChars)")
} catch PasswordError.short(let minChars) where minChars < 8 {
    print("We have a moderate security policy: passwords must be at least \(minChars)")
} catch PasswordError.short(let minChars) {
    print("We have a serious security policy: passwords must be at least \(minChars)")
}

错误传播

如果函数 A 调用函数 B,而函数 B 调用函数 C,那么应该由谁来处理 C 抛出的错误呢?如果您的答案是 “B”,那么您现有的错误处理知识就足够了。

在functionB() 中捕获所有错误,如果想让functionA()对其下方发生的任何错误视而不见,就可以使用这个方案。

enum PasswordError: Error {
    case empty
    case short
    case obvious
}

func functionA() {
    functionB()
}

func functionB() {
    do {
        try functionC()
    } catch {
        print("Error!")
    }
}

func functionC() throws {
    throw PasswordError.short
}

functionB()忽略错误,让错误向上扩散到它自己的调用者,这就是所谓的错误传播。

enum PasswordError: Error {
    case empty
    case short
    case obvious
}

func functionA() {
    do {
        try functionB()
    } catch {
        print("Error!")
    }
}

func functionB() throws {
	// 向上抛出错误
    try functionC()
}

func functionC() throws {
    throw PasswordError.short
}

将错误处理的一部分委托给最合适的函数。例如,您可能希望functionB()捕捉空密码,而functionA()处理所有其他错误。

func functionA() {
    do {
        try functionB()
    } catch {
        print("Error!")
    }
}

func functionB() throws {
    do {
        try functionC()
    } catch PasswordError.empty {
        print("Empty password!")
    }
}

func functionC() throws {
    throw PasswordError.short
}

抛出函数作为参数

非抛出函数是抛出函数的子类型。因此,您可以在与抛出函数相同的地方使用非抛出函数。

enum Failure: Error {
    case badNetwork(message: String)
    case broken
}

func fetchRemote() throws -> String {
    // complicated, failable work here
    throw Failure.badNetwork(message: "Firewall blocked port.")
}

func fetchLocal() -> String {
    // this won't throw
    return "Taylor"
}

func fetchUserData(using closure: () throws -> String) {
    do {
        let userData = try closure()
        print("User data received: \(userData)")
    } catch Failure.badNetwork(let message) {
        print(message)
    } catch {
        print("Fetch error")
    }
}

fetchUserData(using: fetchLocal)

只需修改最后一行,就可以从本地获取转为远程获取

fetchUserData(using: fetchRemote)

当你想在抛出闭包参数时使用错误传播时,需要使用rethrows关键字

func fetchUserData(using closure: () throws -> String) rethrows {
    let userData = try closure()
    print("User data received: \(userData)")
}

try try? try!

Swift 的错误处理有三种形式,三种形式都有其用途。它们都用于调用标记有throws 的函数,但意义却有细微的不同:

  1. 使用try时,必须有一个catch块来处理发生的任何错误。
  2. 使用try?时,如果出现任何错误,您调用的函数将自动返回nil。您不需要捕获它们,但需要注意您的返回值变成了可选值。
  3. 使用try!时,如果出现任何错误,函数将使应用程序崩溃。

使用 try? 意味着安全地解包其可选的返回值

if let savedText = try? String(contentsOfFile: "saved.txt") {
    loadText(savedText)
} else {
    showFirstRunScreen()
}

如果返回值为 nil,您还可以使用空合操作符??来使用默认值,从而完全消除可选性。

let savedText = (try? String(contentsOfFile: "saved.txt")) ?? "Hello, world!"

断言

断言允许声明某些条件必须为真。断言常见的两种形式

assert(condition: Bool)
assert(condition: Bool, message: String)

断言的基本使用

let success = runImportantOperation()
assert(success == true, "Important operation failed!")

先决条件

只有当应用程序在调试模式下运行时,才会检查断言,这在开发过程中非常有用,但在发布模式下会自动停用。如果您希望在发布模式下进行断言,考虑到失败会导致应用程序立即终止,您应该使用precondition()代替。

func *(lhs: [Int], rhs: [Int]) -> [Int] {
    precondition(lhs.count == rhs.count, "Arrays were not the same size")

    var result = [Int]()

    for (index, int) in lhs.enumerated() {
        result.append(int * rhs[index])
    }

    return result
}

let a = [1, 2, 3]
let b = [4, 5]
let c = a * b

请记住,启用-Ounchecked会有效地禁用您的先决条件,但同时也会禁用其他边界校验。

Never 和 fatalError()

Never返回类型,它的意思是 “这个函数永远不会返回”。这不同于Void,后者的意思是 “什么也不会返回”–Never函数实际上永远不会返回。

fatalError()函数,该函数会立即终止应用程序,并可选择写出错误信息。

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容