Swift 6.0 如何更优雅的抛出和处理特定类型的错误

概述

从 Swift 语言诞生那天儿起,它就不厌其烦一遍又一遍地向秃头码农们诉说着自己的类型安全和高雅品味。

不过遗憾的是,作为 Swift 语言中错误处理 这最为重要的一环却时常让小伙伴们不得要领、满腹狐疑。

在本篇博文中,您将学到如下内容:

  • 概述
  • [1. Swift 6.0 之前的错误机制](#1. Swift 6.0 之前的错误机制)
  • [2. Swift 6.0 全新的特定错误抛出机制](#2. Swift 6.0 全新的特定错误抛出机制)
  • [3. 旧机制的向后兼容性](#3. 旧机制的向后兼容性)
  • 总结

从 Swift 6.0 开始,苹果为 Swift 添加了全新的错误类型限定机制,我们从此对各种方法究竟抛出何种错误将会始终了然于胸、一目了然。

闲言少叙,让我们马上开始错误抛出大冒险吧!

Let's go!!!😉


本博文对应的视频课在此,欢迎小伙伴们恣意观看!

Swift 6.0 如何更优雅的抛出和处理特定类型的错误


1. Swift 6.0 之前的错误机制

众所周知,为任何语言提供错误处理之道都是至关重要的"硬核"操作,Swift 自然也不例外。

在 Swift 6.0 之前,我们可以这样考虑特定方法发生错误时的处理方式:

swift 复制代码
enum FooError: Error {
    case tooBig
    case tooSmall
}

func foo() throws -> Int {
    let value = Int.random(in: 1...100)
    
    guard value < 60 else {
        throw FooError.tooBig
    }
    
    guard value > 20 else {
        throw FooError.tooSmall
    }
    
    return value
}

从上面的代码可以看到,我们定义了一个 FooError 错误类型,然后在 foo 方法中根据实际情况抛出其中的错误。

我们可以这样调用 foo 方法:

swift 复制代码
func boo() {
    do {
        let value = try foo()
        print(value)
    } catch let error as FooError {
        switch error {
        case .tooBig: print("Too big...")
        case .tooSmall: print("Too small...")
        }
    } catch {
        print("General erro: \(error)")
    }
}

如您所见:我们使用 do...catch 语法块捕获了 foo 方法调用时可能发生的错误。注意,为了准确我们需要显式"解包"捕获的错误类型。这样会带来几个问题:

  • 如果不看 foo 方法的源代码,无法确切知道它到底会抛出什么样的错误;
  • catch 必须指定具体的错误类型;(当然不指定也可以,但这种情况不在本篇考虑之内)
  • 无法在编译期间发现不适当的错误捕获代码;

为了不让头发已经所剩无几的秃头码农们再揪心于任意方法可能犯下的"弥天大错",Swift 必须做些什么!

That's Right!!!

2. Swift 6.0 全新的特定错误抛出机制

好消息来了:从 Swift 6.0 开始我们可以"名正言顺"的让方法抛出指定类型的错误啦!这是通过在方法签名中指明错误类型来实现的:

swift 复制代码
enum FooError: Error {
    case tooBig
    case tooSmall
}

func foo() throws(FooError) -> Int {
    let value = Int.random(in: 1...100)
    
    guard value < 60 else {
        throw FooError.tooBig
    }
    
    guard value > 20 else {
        throw FooError.tooSmall
    }
    
    return value
}

在上面的代码中,我们紧接着在 foo 方法定义的 throws 关键字后面添加了可能抛出的错误类型。这样做的好处是:同时让编译器和调用者单凭方法签名就能明了该方法可能抛出的错误了

而且这样一来,由于编译器对可能抛出的错误已然一清二楚,所以我们捕获 foo 方法错误的代码还可以进一步简化:

swift 复制代码
func boo() {
    do {
        let value = try foo()
        print(value)
    } catch {
        switch error {
        case .tooBig: print("Too big...")
        case .tooSmall: print("Too small...")
        }
    }
}

看到了吗?有了方法抛出错误类型的显式申明之后,我们在 catch 子句中无需再喋喋不休的"解包 Unwrap"实际的错误类型了!是不是很赞呢?

3. 旧机制的向后兼容性

在 Swift 6.0 中,之前的错误抛出和处理机制仍然被延续下来,从而做到连贯而统一。

比如下面的代码:

swift 复制代码
func foo() throws {}

在 Swift 6.0 中会在"背后"悄悄变为如下形式:

swift 复制代码
func foo() throws(any Error) {}

这意味着,如果我们在 Swift 6.0 中为不可抛出错误(throws)的方法限定错误类型,Swift 就视其为 any Error。

甚至原本绝不会抛出错误的方法:

swift 复制代码
func foo() {}

在 Swift 6.0 中也会"偷偷"抛出一个"所谓的" Never 错误类型:

swift 复制代码
func foo() throws(Never) {}

看到这里,小伙伴们是否对 Swift 6.0 中全新的错误抛出和处理机制一清二楚了呢?棒棒哒!💯


想要系统学习 Swift 的各位小伙伴们,赶快到我的《Swift语言开发精讲》专栏来逛一逛吧:


总结

在本篇博文中,我们讨论了在 Swift 6.0 中如何更加优雅的抛出和处理指定类型的错误,并顺便聊了聊 Swift 6.0 之前的旧机制如何一致的做到向后兼容。

感谢观赏,再会啦!😎

相关推荐
zaim14 天前
Python 的 Decimal的错误计算
python·error·误差·decimal·精度·getcontext
Hdnw2 个月前
Java异常体系结构
java·开发语言·error
duration~2 个月前
前端发送了请求头的参数,经debug发现后端请求对象请求头中没有该参数
error
Amd7942 个月前
Nuxt.js 应用中的 kit:compatibility 事件钩子详解
浏览器·开发·应用·nuxt.js·插件·兼容性·钩子
许野平3 个月前
Rust:Result 和 Error
开发语言·后端·rust·error·result
JohnsonXin3 个月前
【兼容性记录】video标签在 IOS 和 安卓中的问题
android·前端·css·ios·h5·兼容性
许野平3 个月前
Rust:深入浅出说一说 Error 类型
java·开发语言·rust·error
Amd7943 个月前
使用 Nuxt Kit 检查模块与 Nuxt 版本兼容性
模块·nuxt·兼容性·版本·nuxt3·nuxt2·检查
图灵追慕者4 个月前
paddlepaddle 和torch 还有yolov8三种框架兼容性
人工智能·yolo·paddlepaddle·兼容性