Swift 错误处理:throw、throws 与 try 的完整指南#
1. 核心概念#
Swift 错误处理三要素#
| 关键字/机制 | 作用 | 阶段 |
|---|---|---|
throws | 声明函数可能抛出错误 | 函数声明 |
throw | 主动抛出错误实例 | 函数实现 |
try | 调用可能抛出错误的函数 | 函数调用 |
do-catch | 捕获和处理错误 | 错误处理 |
2. 函数声明与 throws#
基本语法#
func functionName(parameters) throws -> ReturnType {
// 实现可能抛出错误的代码
}关键特征#
throws关键字的位置:在参数列表之后,返回类型之前- 可与其他修饰符组合:
async throws -> Data // 异步+可能抛出错误 mutating throws -> Void // 可变方法+可能抛出错误
返回值类型示例#
| 声明形式 | 含义 |
|---|---|
func a() -> String | 返回 String,不抛出错误 |
func b() throws -> String | 返回 String,可能抛出错误 |
func c() throws | 没有返回值,可能抛出错误(等价于 throws -> Void) |
3. 错误抛出 (throw)#
错误类型要求#
必须实现 Error 协议:
enum NetworkError: Error {
case invalidURL
case timeout(duration: Int)
case serverError(code: Int)
}
struct ParsingError: Error {
let line: Int
let column: Int
}抛出方式#
func validateInput(_ input: String) throws {
guard !input.isEmpty else {
throw ValidationError.emptyInput
}
guard input.count >= 8 else {
throw ValidationError.tooShort(minLength: 8)
}
}4. 错误捕获 (try)#
try 的三重形态#
| 形式 | 行为 | 返回值类型 | 错误处理方式 |
|---|---|---|---|
try | 标准形式 | 原类型 | 必须用 do-catch |
try? | 忽略错误 | Optional | 错误时返回 nil |
try! | 强制解包(危险) | 非可选类型 | 错误时崩溃 |
标准捕获模式#
do {
let data = try parseData(rawInput)
let result = try process(data)
print("Success: \(result)")
} catch ValidationError.emptyInput {
print("Error: Input cannot be empty")
} catch let ValidationError.tooShort(minLength) {
print("Error: Need at least \(minLength) characters")
} catch {
print("Unexpected error: \(error)")
}5. 返回值与错误的关系#
返回值的存在性#
// 可能返回 String 或抛出错误
func fetchString() throws -> String {
if successCondition {
return "Data"
} else {
throw FetchError.failed
}
}
// 没有返回值,只有成功/失败
func saveToDisk() throws {
guard hasPermission else {
throw DiskError.permissionDenied
}
// 保存操作...
}特殊返回值模式#
// 返回 Optional,错误时返回 nil
func safeFetch() -> String? {
return try? dangerousFetch()
}
// 返回 Result 类型
func modernFetch() -> Result<String, Error> {
do {
let value = try dangerousFetch()
return .success(value)
} catch {
return .failure(error)
}
}6. 高级场景与最佳实践#
链式错误处理#
func complexOperation() throws -> FinalResult {
let rawData = try fetchData() // 可能抛出 NetworkError
let parsed = try parse(rawData) // 可能抛出 ParsingError
return try transform(parsed) // 可能抛出 TransformationError
}
// 统一处理所有可能的错误
do {
let result = try complexOperation()
} catch let error as NetworkError {
// 处理网络错误
} catch let error as ParsingError {
// 处理解析错误
} catch {
// 其他错误
}异步错误处理#
func asyncFetch() async throws -> Data {
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
Task {
do {
let data = try await asyncFetch()
// 使用数据
} catch {
// 处理错误
}
}重新抛出 (rethrows)#
func mapValues<T>(_ transform: (Element) throws -> T) rethrows -> [T] {
var result = [T]()
for element in self {
result.append(try transform(element))
}
return result
}
// 使用示例
let numbers = [1, 2, 3]
let strings1 = numbers.mapValues { String($0) } // 不需要 try
let strings2 = numbers.mapValues { throw SomeError() } // 需要 try7. 常见误区与调试技巧#
典型错误模式#
错误的位置错误:
// ❌ 错误:throws 应该在返回类型前 func wrong() -> throws String { ... } // ✅ 正确 func correct() throws -> String { ... }忽略必要错误处理:
// ❌ 编译错误:未处理可能的错误 let data = parseData(rawInput) // ✅ 正确处理 let data = try? parseData(rawInput)过度使用
try!:// ❌ 危险:可能导致崩溃 let data = try! parseData(untrustedInput) // ✅ 安全处理 if let data = try? parseData(input) { // 处理数据 }
调试建议#
使用
catch块打印错误堆栈:do { try riskyOperation() } catch { print("Error occurred: \(error)") dump(error) // 查看完整错误信息 Thread.callStackSymbols.forEach { print($0) } // 打印调用堆栈 }断点设置:
- 在 Xcode 中添加 Exception Breakpoint
- 使用条件断点捕获特定错误类型
错误传播跟踪:
func debugTry<T>(_ expression: () throws -> T) -> T { do { return try expression() } catch { print("Error propagated: \(error)") throw error // 继续传递错误 } } // 使用示例 let result = try debugTry { try parseData(input) }
总结表格#
| 场景 | 推荐模式 | 应避免的模式 |
|---|---|---|
| 简单错误处理 | do-catch 基本块 | 忽略 try |
| 可选值处理 | try? + Optional Binding | 强制解包 try! |
| 错误传播 | rethrows + 高阶函数 | 多层嵌套 do-catch |
| 异步操作 | async throws + Task | 混合回调与错误处理 |
| 单元测试 | XCTAssertThrowsError | 不验证错误条件 |
通过本指南,您应该能够:
- 正确声明和使用抛出错误的函数
- 合理选择
try、try?或try! - 设计清晰的错误传播路径
- 编写健壮的异步错误处理代码
- 避免常见错误处理陷阱
建议结合具体项目需求选择错误处理策略,在代码安全性和简洁性之间找到最佳平衡点。