Swift 中的错误处理:try/catch 及其他 — 完整指南
错误处理是 iOS 和 macOS 开发中的关键主题之一。Swift 语言实现了一套强大而优雅的故障管理系统,远远超出了简单的 try/catch。理解这些机制可以让你编写出可靠、可预测且安全的代码。在本文中,我们将剖析所有方面:从基本的错误声明到使用可选值和反初始化器的高级技术。
基础:Error 协议和 throws 关键字
在 Swift 中,任何错误都必须符合 Error 协议。通常使用枚举 (enum) 来实现这一点。让我们看一个简单的例子——密码验证:
enum PasswordError: Error { case tooShort case missingDigit case missingUppercase}
func validatePassword(_ password: String) throws { guard password.count >= 8 else { throw PasswordError.tooShort } guard password.rangeOfCharacter(from: .decimalDigits) != nil else { throw PasswordError.missingDigit } guard password.rangeOfCharacter(from: .uppercaseLetters) != nil else { throw PasswordError.missingUppercase } print("密码正确")}函数声明中的 throws 关键字告诉编译器,执行可能会被错误中断。调用此类函数需要特殊的语法。
do-catch 结构和 try 关键字
为了处理潜在的错误,使用带有 try 标记的 do-catch 块:
do { try validatePassword("Hello123") print("成功!")} catch PasswordError.tooShort { print("错误:密码太短")} catch PasswordError.missingDigit { print("错误:密码中没有数字")} catch PasswordError.missingUppercase { print("错误:密码中没有大写字母")} catch { print("未知错误:\\(error)")}请注意最后一个 catch 块——它捕获任何之前未处理到的错误。这是一个强制性要求:Swift 编译器会检查所有可能的错误是否都已处理。
try? 和 try! — 简化的错误处理方式
有时你不需要对错误进行完全控制。Swift 提供了两种简写形式:
- try? — 将错误转换为可选值
nil。如果没有错误,则返回包装在Optional中的结果。 - try! — 断言不会发生错误。如果错误确实发生,应用程序将崩溃(运行时崩溃)。
// try? — 安全地忽略错误if let _ = try? validatePassword("weak") { print("密码验证通过")} else { print("密码未通过")}
// try! — 仅当你 100% 确定时使用let result = try! validatePassword("StrongPass1") // 如果密码无效则崩溃仅在理论上不可能出错的情况下使用 try!(例如,加载应用程序捆绑包中的资源时)。
高级技术:rethrows、defer 和 Result
重新抛出错误 (rethrows)
rethrows 关键字应用于高阶函数,这些函数接受可能抛出错误的闭包。函数本身不抛出错误,但传递来自闭包的错误:
func processItems(_ items: [String], using validator: (String) throws -> Void) rethrows { for item in items { try validator(item) }}
// 使用do { try processItems(["Hello", "World123"], using: validatePassword)} catch { print("处理错误:\\(error)")}这允许编写灵活的包装函数,而无需重复错误处理代码。