错误处理 #

一、错误处理概述 #

Swift提供了强大的错误处理机制,用于响应和处理运行时错误。

1.1 错误处理方式 #

  • 抛出错误(throw)
  • 捕获错误(try-catch)
  • 传播错误(rethrows)
  • 可选值处理(try?)

二、Error协议 #

2.1 定义错误类型 #

swift
enum NetworkError: Error {
    case noConnection
    case timeout
    case serverError(Int)
    case invalidURL
}

enum ValidationError: Error {
    case emptyInput
    case invalidLength
    case invalidFormat
}

2.2 关联值错误 #

swift
enum FileError: Error {
    case fileNotFound(path: String)
    case permissionDenied
    case readError(description: String)
}

func readFile(at path: String) throws -> String {
    throw FileError.fileNotFound(path: path)
}

三、抛出错误 #

3.1 throw关键字 #

swift
func divide(_ a: Int, by b: Int) throws -> Int {
    guard b != 0 else {
        throw NSError(domain: "DivisionError", code: -1, userInfo: [NSLocalizedDescriptionKey: "除数不能为零"])
    }
    return a / b
}

3.2 自定义错误 #

swift
enum DivisionError: Error, LocalizedError {
    case divisionByZero
    
    var errorDescription: String? {
        switch self {
        case .divisionByZero:
            return "除数不能为零"
        }
    }
}

func divide(_ a: Int, by b: Int) throws -> Int {
    guard b != 0 else {
        throw DivisionError.divisionByZero
    }
    return a / b
}

四、捕获错误 #

4.1 do-catch语句 #

swift
do {
    let result = try divide(10, by: 0)
    print(result)
} catch {
    print("错误: \(error)")
}

4.2 多catch块 #

swift
enum NetworkError: Error {
    case noConnection
    case timeout
    case serverError(Int)
}

func fetchData() throws -> Data {
    throw NetworkError.serverError(500)
}

do {
    let data = try fetchData()
    print(data)
} catch NetworkError.noConnection {
    print("无网络连接")
} catch NetworkError.timeout {
    print("请求超时")
} catch NetworkError.serverError(let code) {
    print("服务器错误: \(code)")
} catch {
    print("未知错误: \(error)")
}

4.3 try?和try! #

swift
func divide(_ a: Int, by b: Int) throws -> Int {
    guard b != 0 else {
        throw DivisionError.divisionByZero
    }
    return a / b
}

let result1 = try? divide(10, by: 2)
print(result1 ?? 0)

let result2 = try? divide(10, by: 0)
print(result2 ?? 0)

let result3 = try! divide(10, by: 2)
print(result3)

五、错误传播 #

5.1 throws函数 #

swift
func validateInput(_ input: String?) throws -> String {
    guard let input = input, !input.isEmpty else {
        throw ValidationError.emptyInput
    }
    
    guard input.count >= 3 else {
        throw ValidationError.invalidLength
    }
    
    return input
}

func processInput(_ input: String?) throws {
    let validInput = try validateInput(input)
    print("处理输入: \(validInput)")
}

5.2 rethrows #

swift
func process<T>(_ value: T, using transform: (T) throws -> T) rethrows -> T {
    return try transform(value)
}

let result = try process(5) { value in
    guard value > 0 else {
        throw NSError(domain: "", code: -1)
    }
    return value * 2
}

六、Result类型 #

6.1 基本用法 #

swift
enum NetworkError: Error {
    case noConnection
    case serverError
}

func fetchData(completion: @escaping (Result<Data, NetworkError>) -> Void) {
    let success = Bool.random()
    
    if success {
        completion(.success(Data()))
    } else {
        completion(.failure(.serverError))
    }
}

fetchData { result in
    switch result {
    case .success(let data):
        print("成功: \(data)")
    case .failure(let error):
        print("失败: \(error)")
    }
}

6.2 Result方法 #

swift
let result: Result<Int, Error> = .success(42)

let mapped = result.map { $0 * 2 }
print(mapped)

let flatMapped = result.flatMap { value in
    return .success(value + 10)
}
print(flatMapped)

do {
    let value = try result.get()
    print(value)
} catch {
    print(error)
}

6.3 实际应用 #

swift
struct User: Decodable {
    let id: Int
    let name: String
}

func fetchUser(id: Int) -> Result<User, Error> {
    let json = """
    {"id": 1, "name": "张三"}
    """
    
    guard let data = json.data(using: .utf8) else {
        return .failure(NSError(domain: "", code: -1))
    }
    
    do {
        let user = try JSONDecoder().decode(User.self, from: data)
        return .success(user)
    } catch {
        return .failure(error)
    }
}

let result = fetchUser(id: 1)
switch result {
case .success(let user):
    print(user.name)
case .failure(let error):
    print(error)
}

七、defer语句 #

7.1 基本用法 #

swift
func processFile() throws {
    print("开始处理")
    
    defer {
        print("清理资源")
    }
    
    print("处理中...")
    
    if Bool.random() {
        throw NSError(domain: "", code: -1)
    }
    
    print("处理完成")
}

do {
    try processFile()
} catch {
    print("捕获错误")
}

7.2 多个defer #

swift
func multipleDefer() {
    defer { print("第一个defer") }
    defer { print("第二个defer") }
    defer { print("第三个defer") }
    
    print("函数体")
}

multipleDefer()

八、实际应用 #

8.1 网络请求 #

swift
enum APIError: Error {
    case invalidURL
    case noData
    case decodingError
}

class APIClient {
    func request<T: Decodable>(url: URL) async throws -> T {
        let (data, response) = try await URLSession.shared.data(from: url)
        
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode) else {
            throw APIError.noData
        }
        
        do {
            return try JSONDecoder().decode(T.self, from: data)
        } catch {
            throw APIError.decodingError
        }
    }
}

8.2 文件操作 #

swift
enum FileError: Error {
    case fileNotFound
    case readError
    case writeError
}

func readTextFile(at path: String) throws -> String {
    guard let data = FileManager.default.contents(atPath: path) else {
        throw FileError.fileNotFound
    }
    
    guard let text = String(data: data, encoding: .utf8) else {
        throw FileError.readError
    }
    
    return text
}

func writeTextFile(at path: String, content: String) throws {
    guard let data = content.data(using: .utf8) else {
        throw FileError.writeError
    }
    
    try data.write(to: URL(fileURLWithPath: path))
}

8.3 表单验证 #

swift
enum FormError: Error {
    case emptyField(String)
    case invalidEmail
    case passwordTooShort
    case passwordsDoNotMatch
}

struct FormData {
    var email: String
    var password: String
    var confirmPassword: String
}

func validateForm(_ data: FormData) throws -> Bool {
    guard !data.email.isEmpty else {
        throw FormError.emptyField("邮箱")
    }
    
    guard data.email.contains("@") else {
        throw FormError.invalidEmail
    }
    
    guard data.password.count >= 8 else {
        throw FormError.passwordTooShort
    }
    
    guard data.password == data.confirmPassword else {
        throw FormError.passwordsDoNotMatch
    }
    
    return true
}

九、总结 #

本章学习了Swift错误处理:

  • Error协议:定义错误类型
  • throw:抛出错误
  • do-catch:捕获错误
  • Result:封装结果

最佳实践:

  • 使用枚举定义错误
  • 提供有意义的错误信息
  • 合理使用try?和try!
  • 使用defer清理资源

下一章,我们将学习实践应用!

最后更新:2026-03-26