Swift 错误处理 (Error Handling)
📚 目录
📖 内容概述
错误处理是Swift编程语言中的重要特性,它提供了一种优雅的方式来处理运行时可能出现的错误情况。Swift的错误处理机制基于抛出、捕获、传播和操作可恢复错误。
🔍 详细内容
错误处理基础
错误的概念
Swift中的错误处理涉及四个关键字:
throw
:抛出错误throws
:标记可能抛出错误的函数try
:调用可能抛出错误的函数catch
:捕获并处理错误
错误类型
开发过程中的常见错误
swift
// 1. 语法错误(编译时报错)
// let x = 10 + // 语法错误,会在编译时发现
// 2. 逻辑错误(程序运行但结果不正确)
func add(a: Int, b: Int) -> Int {
return a * b // 逻辑错误:应该是加法但写成了乘法
}
// 3. 运行时错误(可能导致程序崩溃)
// let array = [1, 2, 3]
// let item = array[10] // 运行时错误:数组越界
自定义错误
定义错误类型
swift
// 使用枚举定义错误类型
enum ValidationError: Error {
case emptyString
case tooShort(minimum: Int)
case tooLong(maximum: Int)
case invalidFormat
}
enum FileError: Error {
case notFound
case permissionDenied
case corrupted
case networkError(String)
}
enum MathError: Error {
case divisionByZero
case negativeSquareRoot
case overflow
case underflow
}
使用结构体定义错误
swift
struct CustomError: Error {
let code: Int
let message: String
let underlyingError: Error?
init(code: Int, message: String, underlyingError: Error? = nil) {
self.code = code
self.message = message
self.underlyingError = underlyingError
}
}
抛出错误
基本抛出
swift
func divide(_ numerator: Int, by denominator: Int) throws -> Double {
if denominator == 0 {
throw MathError.divisionByZero
}
return Double(numerator) / Double(denominator)
}
func validatePassword(_ password: String) throws -> Bool {
if password.isEmpty {
throw ValidationError.emptyString
}
if password.count < 6 {
throw ValidationError.tooShort(minimum: 6)
}
if password.count > 20 {
throw ValidationError.tooLong(maximum: 20)
}
return true
}
复杂错误处理
swift
func processFile(at path: String) throws -> String {
// 检查文件是否存在
guard FileManager.default.fileExists(atPath: path) else {
throw FileError.notFound
}
// 检查文件权限
guard FileManager.default.isReadableFile(atPath: path) else {
throw FileError.permissionDenied
}
// 读取文件内容
do {
let content = try String(contentsOfFile: path)
return content
} catch {
throw FileError.corrupted
}
}
处理错误
基本错误处理
swift
func testDivision() {
do {
let result = try divide(10, by: 2)
print("结果: \(result)")
} catch MathError.divisionByZero {
print("错误: 除数不能为零")
} catch {
print("其他错误: \(error)")
}
}
do-catch语句
详细的错误捕获
swift
func handleValidation() {
do {
try validatePassword("123")
print("密码验证通过")
} catch ValidationError.emptyString {
print("密码不能为空")
} catch ValidationError.tooShort(let minimum) {
print("密码太短,至少需要 \(minimum) 个字符")
} catch ValidationError.tooLong(let maximum) {
print("密码太长,最多允许 \(maximum) 个字符")
} catch ValidationError.invalidFormat {
print("密码格式不正确")
} catch {
print("未知错误: \(error)")
}
}
多个错误条件处理
swift
func processMultipleOperations() {
do {
let result1 = try divide(10, by: 2)
let result2 = try divide(20, by: 4)
let finalResult = result1 + result2
print("最终结果: \(finalResult)")
} catch MathError.divisionByZero {
print("除法错误:除数为零")
} catch {
print("操作失败: \(error)")
}
}
错误类型判断
swift
func handleFileOperation() {
do {
let content = try processFile(at: "/path/to/file.txt")
print("文件内容: \(content)")
} catch let error as FileError {
switch error {
case .notFound:
print("文件不存在")
case .permissionDenied:
print("没有文件读取权限")
case .corrupted:
print("文件已损坏")
case .networkError(let message):
print("网络错误: \(message)")
}
} catch {
print("其他错误: \(error)")
}
}
try?和try!
try? - 可选的错误处理
swift
func safeOperation() {
// try? 将错误转换为可选值
let result1 = try? divide(10, by: 2) // Optional(5.0)
let result2 = try? divide(10, by: 0) // nil
print("结果1: \(result1 ?? 0)")
print("结果2: \(result2 ?? 0)")
}
// try? 等价于以下代码
func equivalentOperation() {
var result: Double?
do {
result = try divide(10, by: 2)
} catch {
result = nil
}
print("结果: \(result ?? 0)")
}
try! - 强制错误处理
swift
func forcedOperation() {
// try! 假设操作不会失败,如果失败则程序崩溃
let result = try! divide(10, by: 2) // 5.0
print("结果: \(result)")
// 危险的用法 - 如果失败会导致程序崩溃
// let badResult = try! divide(10, by: 0) // 运行时崩溃
}
defer语句
清理资源
swift
func readFile(fileName: String) throws -> String {
let file = FileHandle(forReadingAtPath: fileName)
defer {
file?.closeFile()
print("文件已关闭")
}
guard let file = file else {
throw FileError.notFound
}
let data = file.readDataToEndOfFile()
return String(data: data, encoding: .utf8) ?? ""
}
多个defer语句
swift
func complexOperation() throws {
print("开始复杂操作")
defer {
print("清理操作1")
}
defer {
print("清理操作2")
}
defer {
print("清理操作3")
}
// 模拟一些操作
throw ValidationError.emptyString
}
// 输出顺序:
// 开始复杂操作
// 清理操作3
// 清理操作2
// 清理操作1
错误传播
错误向上传播
swift
func lowLevelOperation() throws -> String {
throw ValidationError.emptyString
}
func midLevelOperation() throws -> String {
return try lowLevelOperation()
}
func highLevelOperation() throws -> String {
return try midLevelOperation()
}
func handlePropagation() {
do {
let result = try highLevelOperation()
print("操作成功: \(result)")
} catch {
print("操作失败: \(error)")
}
}
错误转换
swift
func convertError() throws -> String {
do {
return try lowLevelOperation()
} catch ValidationError.emptyString {
throw CustomError(code: 100, message: "输入验证失败")
}
}
实践示例
网络请求错误处理
swift
enum NetworkError: Error {
case noConnection
case serverError(Int)
case invalidResponse
case decodingError
}
class NetworkManager {
func fetchData(from url: URL) throws -> Data {
// 模拟网络请求
let isConnected = true
let statusCode = 200
guard isConnected else {
throw NetworkError.noConnection
}
guard statusCode == 200 else {
throw NetworkError.serverError(statusCode)
}
return Data()
}
func fetchUserData(userId: Int) throws -> User {
let url = URL(string: "https://api.example.com/users/\(userId)")!
let data = try fetchData(from: url)
do {
return try JSONDecoder().decode(User.self, from: data)
} catch {
throw NetworkError.decodingError
}
}
}
struct User: Codable {
let id: Int
let name: String
}
表单验证错误处理
swift
class FormValidator {
func validateEmail(_ email: String) throws -> Bool {
if email.isEmpty {
throw ValidationError.emptyString
}
if !email.contains("@") {
throw ValidationError.invalidFormat
}
return true
}
func validateForm(email: String, password: String) -> [Error] {
var errors: [Error] = []
do {
try validateEmail(email)
} catch {
errors.append(error)
}
do {
try validatePassword(password)
} catch {
errors.append(error)
}
return errors
}
}
最佳实践
1. 错误类型设计
swift
// 好的做法:使用枚举定义相关错误
enum DatabaseError: Error {
case connectionFailed
case queryFailed(String)
case dataCorrupted
case timeout
}
// 避免:使用通用错误类型
// struct GenericError: Error { let message: String }
2. 错误信息
swift
extension ValidationError: LocalizedError {
var errorDescription: String? {
switch self {
case .emptyString:
return "输入不能为空"
case .tooShort(let minimum):
return "输入长度不能少于\(minimum)个字符"
case .tooLong(let maximum):
return "输入长度不能超过\(maximum)个字符"
case .invalidFormat:
return "输入格式不正确"
}
}
}
3. 错误处理策略
swift
// 策略1:立即处理错误
func immediateHandling() {
do {
let result = try divide(10, by: 0)
print("结果: \(result)")
} catch {
print("发生错误,使用默认值")
let defaultResult = 0.0
print("结果: \(defaultResult)")
}
}
// 策略2:传播错误
func propagateError() throws {
let result = try divide(10, by: 0)
print("结果: \(result)")
}
// 策略3:转换为可选值
func optionalHandling() {
let result = try? divide(10, by: 0)
print("结果: \(result ?? 0)")
}
📝 重要提示
- 错误类型 :实现
Error
协议,通常使用枚举 - 函数声明 :使用
throws
标记可能抛出错误的函数 - 错误抛出 :使用
throw
关键字抛出错误 - 错误处理 :使用
try
、do-catch
语句处理错误 - 资源清理 :使用
defer
语句确保资源被正确释放 - 错误传播:错误会自动向上传播直到被捕获
🎯 总结
Swift的错误处理机制提供了一种类型安全且表达力强的方式来处理运行时错误。通过合理使用错误处理,我们可以:
- 编写更健壮的代码
- 提供更好的用户体验
- 更容易调试和维护代码
- 避免程序意外崩溃
掌握错误处理是编写高质量Swift代码的重要技能。
本文档基于Swift 5.0+版本,涵盖了错误处理的核心概念和最佳实践。