iOS 控制流 之 错误传播

在 Swift 中,throwsrethrowsneverNever 类型)是与错误处理相关的关键概念,它们分别用于不同场景下的错误传播和控制流。以下是它们的详细对比和用法解析:


1. throws:声明可能抛出错误的函数

作用

  • 标记一个函数、方法或闭包可能抛出错误(Error 协议类型)。
  • 调用方必须使用 trydo-catchtry?/try! 处理错误。

语法

swift 复制代码
func mightFail() throws -> Int {
    // 可能抛出错误的代码
    throw SomeError.example
    return 42
}

调用方式

swift 复制代码
// 方式 1: do-catch
do {
    let result = try mightFail()
    print(result)
} catch {
    print("Error: (error)")
}
 
// 方式 2: try? (返回 Optional)
if let result = try? mightFail() {
    print(result)
}
 
// 方式 3: try! (强制解包,崩溃风险)
let result = try! mightFail() // 如果抛出错误,程序崩溃

适用场景

  • 任何可能因外部条件(如网络请求、文件读写)失败的函数。
  • 需要明确告知调用方可能出错的接口。

2. rethrows:重新抛出错误的函数

作用

  • 标记一个高阶函数 (接受闭包作为参数的函数)可能抛出错误,但错误实际来自闭包本身
  • 函数本身不直接抛出错误,只是将闭包中的错误向上传递。
  • 编译器会检查闭包是否可能抛出错误,如果闭包不抛出错误,则调用方也无需处理错误。

语法

swift 复制代码
func process(_ action: () throws -> Void) rethrows {
    try action() // 仅重新抛出闭包中的错误
}

调用方式

swift 复制代码
// 闭包抛出错误时,需用 try 调用 process
do {
    try process {
        throw SomeError.example
    }
} catch {
    print("Error: (error)")
}
 
// 闭包不抛出错误时,无需 try
process {
    print("No error here")
}

throws 的区别

特性 throws rethrows
错误来源 函数内部可能抛出错误 错误仅来自闭包参数
调用要求 调用方必须处理错误(无论闭包是否抛出) 仅当闭包抛出错误时,调用方需处理错误
典型场景 直接操作可能失败的资源(如文件读写) 高阶函数(如 mapforEach 的变体)

示例:rethrows 的实际应用

swift 复制代码
// 高阶函数:仅当闭包抛出错误时,才需要 try
func retry<T>(times: Int, _ action: () throws -> T) rethrows -> T {
    for _ in 0..<times {
        do {
            return try action()
        } catch {
            continue
        }
    }
    throw SomeError.maxRetriesExceeded
}
 
// 调用方式 1:闭包抛出错误
do {
    let result = try retry(times: 3) {
        throw SomeError.example
    }
} catch {
    print("Failed after retries: (error)")
}
 
// 调用方式 2:闭包不抛出错误
let success = try retry(times: 3) {
    return "Success" // 无需 catch
}

3. Never:表示永不返回的错误类型

作用

  • Never 是 Swift 的"底部类型"(Bottom Type),表示函数永远不会正常返回(要么无限执行,要么抛出错误/终止程序)。
  • 常用于标记致命错误无限循环的场景。

语法

swift 复制代码
func crashAndBurn() -> Never {
    fatalError("Something went wrong!") // 终止程序
}

throws 的关系

  • 如果函数返回 Never,则它必须抛出错误或终止程序(不能正常返回)。
  • 编译器会强制检查所有代码路径是否满足 Never 的语义。

示例

swift 复制代码
// 1. 抛出错误(满足 Never 的语义)
func impossible() -> Never {
    throw SomeError.criticalFailure
}
 
// 2. 无限循环(也满足 Never 的语义)
func infiniteLoop() -> Never {
    while true {}
}
 
// 3. 调用 Never 函数时,编译器知道后续代码不可达
func test() {
    impossible() // 调用后,编译器认为后续代码不会执行
    print("This line will never run") // ❌ 编译警告:Unreachable code
}

适用场景

  • 标记不可恢复的错误(如 fatalErrorpreconditionFailure)。
  • 实现无限循环或事件循环(如游戏主循环)。

4. 总结对比

关键字 作用 典型场景
throws 声明函数可能抛出错误,调用方必须处理 文件读写、网络请求等可能失败的操作
rethrows 声明高阶函数可能重新抛出闭包中的错误,调用方仅在闭包抛出错误时需处理 mapforEach 的变体,如 retry
Never 表示函数永不返回(抛出错误或终止程序),编译器强制检查代码路径 fatalError、无限循环、不可恢复错误

5. 最佳实践

  1. 优先使用 throws

    • 对于可能因外部条件失败的函数,明确标记 throws 以提高代码安全性。

    • 示例:

      swift 复制代码
      func readFile(_ path: String) throws -> String {
          guard let content = try? String(contentsOfFile: path) else {
              throw FileError.notFound
          }
          return content
      }
  2. 谨慎使用 rethrows

    • 仅在高阶函数中需要重新抛出闭包错误时使用,避免滥用。

    • 示例:

      swift 复制代码
      func safeExecute(_ action: () throws -> Void) rethrows {
          print("Starting action...")
          try action()
          print("Action completed.")
      }
  3. 避免滥用 Never

    • 仅在确实需要终止程序或实现无限循环时使用。

    • 示例:

      swift 复制代码
      func validateInput(_ input: String) -> Never {
          precondition(input.count > 0, "Input cannot be empty!")
      }

通过合理使用这些关键字,可以写出更安全、更清晰的 Swift 错误处理代码。

相关推荐
空中海6 小时前
iOS 动态分析、抓包与 Frida Hook
ios·职场和发展·蓝桥杯
空中海9 小时前
iOS 静态逆向、IPA 结构与 Mach-O 分析
ios·华为·harmonyos
Mr -老鬼9 小时前
EasyClick 双端自动化智能体|Android&iOS 全平台 EC 脚本开发助手
android·ios·自动化·易点云测·#easyclick·#ios自动化
空中海10 小时前
01. iOS 逆向基础、环境搭建与授权
macos·ios·cocoa
空中海10 小时前
iOS LLDB 调试、Mach-O、Runtime 与二进制分析
macos·ios·cocoa
空中海10 小时前
iOS 防护、加固复测与综合交付
macos·ios·cocoa
懋学的前端攻城狮1 天前
iOS 列表性能优化实战:从 45fps 到 60fps 的蜕变
ios·性能优化·ui kit
斯班奇的好朋友阿法法1 天前
鸿蒙 vs iOS vs 微信小程序:开发平台全面对比
ios·微信小程序·harmonyos
@大迁世界2 天前
14个你现在必须关闭的 iOS 26 设置,不然手机很快被它榨干
macos·ios·智能手机·objective-c·cocoa
YJlio2 天前
10.2.8 以其他账户运行服务(Running services in alternate accounts):为什么“把服务切到某个用户账号下运行”,本质上是在改变服务的整个安全上下文?
python·安全·ios·机器人·django·iphone·7-zip