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() } // 需要 try
7. 常见误区与调试技巧 #
典型错误模式 #
错误的位置错误:
// ❌ 错误: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!
- 设计清晰的错误传播路径
- 编写健壮的异步错误处理代码
- 避免常见错误处理陷阱
建议结合具体项目需求选择错误处理策略,在代码安全性和简洁性之间找到最佳平衡点。