在 Swift 中,@available 是一个属性标注(Attribute),它用来表示一个类、结构体、枚举、函数或者方法等的可用性信息,指示特定的平台和版本。通过这个属性标注,开发者可以对代码中的各个部分设定适用的操作系统平台及最低支持的版本,也可以指定在某个版本中它们已经过期或不推荐使用,甚至是未来某个版本中会被添加的特性。
我们使用苹果的标准 Swift 库的时候,经常遇到 @available ,一个老代码的某个函数调用,系统升级后,可能会发现一个警告,
'oldMethod(text:messages:key:)' is deprecated: Use newMethod() instead
意思就是这个方法或者函数,现在已经被废弃了,最好不要再使用。在这个情况下,往往你其实还可以编译还可以执行。但是这些被废弃的老方法,要么也许有 bug ,或者系统的代码风格,习惯变了,已经被推荐使用了。有的人觉的既然可以编译和执行,何必理会呢?其实不然,有些方法会在某一个操作系统版本废弃,然后在后续的某个大操作系统版本就彻底消失,无法编译和运行。也就是说,废弃声明就是提醒你可以修改代码了,未来这个方法或者类有可能完全不能用了。最好尊重这些声明来写代码。
我们去看系统代码和头文件的时候,会看到很多类似的例子:
1. 指定功能在平台上的最小可用版本: @available(iOS 10, *) func newiOS10Method() { // 这个方法只会在 iOS 10 及以上版本可用 } 2. 标示一个功能在新版本中被弃用: @available(iOS, deprecated: 11.0, message: "Use newMethod instead") func oldMethod() { // 这个方法在 iOS 11 中被标记为弃用 } 3. 标示一个功能在新版本中被废弃或移除: @available(iOS, obsoleted: 11.0, message: "Use anotherMethod instead") func obsoleteMethod() { // 这个方法从 iOS 11 开始就废弃了,调用它会出错 } 4. 标示功能未来新增: @available(iOS 15, *) func futureMethod() { // 这个方法在未来的 iOS 15 版本中会被添加 } 5. 跨多平台设置可用性: @available(iOS 10, macOS 10.12, *) func crossPlatformMethod() { // 这个方法在 iOS 10 和 macOS 10.12 及以上版本都可用 } 6. 为不同的平台指定不同的最小版本: @available(iOS 14, watchOS 7, *) func multiPlatformMethod() { // 对于 iOS 平台,这个方法要求版本 14.0 及以上 // 对于 watchOS 平台,这个方法要求版本 7.0 及以上 }
但是,这是一般情况下我们接触的 @available 和 deprecated。其实我们自己的代码里面也可以用这些用法。比如我最近在给 TinyStudio(最好的Mac字幕软件之一) 增加新的功能,代码里面涉及到如何调用 ChatGPT,以及用 ChatGPT 来进行一些复杂的字幕后续处理操作。
在此之前我不知道 Swift 的 await 和 actor 语法,所以涉及到 ChatGPT 的代码非常复杂,比如一个巨长的文本发给 ChatGPT 需要实现分段,然后一段一段的去调用 ChatGPT 来进行处理。代码就非常复杂,我用递归调用和回调函数的方法来做,非常麻烦。虽然我可以轻松的写出这样的代码,但是这样的代码在调试、调优和异常处理方面简直是噩梦。
我知道了 Swift 的 await 和 actor 语法后,发现这个场景用这些并发语法来写,代码可以变得非常简洁,而且在调试、调优和异常处理方面非常方便。于是我就着手写 ChatGPT 和文本处理库的异步版本(await/actor)。写的时候,原有的函数叫 send ,新版本就叫做 asyncSend,原有的函数叫 format,新的就叫做 asyncFormat。
初步实现以后,我发现原有的 send 和 format 在很多地方都有调用,直接用文字搜索也会遇到一些类似的函数名。于是我就想起了 @available 和 deprecated 语法。
首先,我先把 ChatGPTKit 类和 TextKit 类的新老版本函数,放在不同的 extension 里(不影响执行,但是从代码来看,就可以把不同类型的方法条理清晰的分明白了):
// 遗留版本函数 extension TextKit { func format(textString) { ... // 遗留版本函数 extension TextKit { func asyncFormat(textString) { ...
然后,在遗留版本函数的 extension 声明前,标注 @available 即可:
// 遗留版本函数@available (*, deprecated, message: "老版本已废弃,请使用异步版本") extension TextKit { func format(textString) { ...
这时候编译程序,就会发现还在用老版本函数的代码就会被编译器发出警告,然后我们根据警告信息,一一替换为新版本就好了。如下: