15-错误处理

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)")
}

📝 重要提示

  1. 错误类型 :实现 Error 协议,通常使用枚举
  2. 函数声明 :使用 throws 标记可能抛出错误的函数
  3. 错误抛出 :使用 throw 关键字抛出错误
  4. 错误处理 :使用 trydo-catch 语句处理错误
  5. 资源清理 :使用 defer 语句确保资源被正确释放
  6. 错误传播:错误会自动向上传播直到被捕获

🎯 总结

Swift的错误处理机制提供了一种类型安全且表达力强的方式来处理运行时错误。通过合理使用错误处理,我们可以:

  • 编写更健壮的代码
  • 提供更好的用户体验
  • 更容易调试和维护代码
  • 避免程序意外崩溃

掌握错误处理是编写高质量Swift代码的重要技能。


本文档基于Swift 5.0+版本,涵盖了错误处理的核心概念和最佳实践。

相关推荐
形影相吊1 小时前
iOS防截屏实战
ios
吴Wu涛涛涛涛涛Tao1 小时前
Flutter 弹窗解析:从系统 Dialog 到完全自定义
flutter·ios
kymjs张涛4 小时前
零一开源|前沿技术周报 #7
android·前端·ios
思考着亮6 小时前
9.方法
ios
思考着亮6 小时前
6.结构体和类
ios
思考着亮6 小时前
7.闭包
ios
咕噜签名分发冰淇淋8 小时前
申请注册苹果iOS企业级开发者证书需要公司拥有什么规模条件
macos·ios·cocoa
2501_9159184118 小时前
Fiddler中文版全面评测:功能亮点、使用场景与中文网资源整合指南
android·ios·小程序·https·uni-app·iphone·webview
不知名It水手20 小时前
uniapp运行项目到ios基座
ios·uni-app·cocoa