Swift ABI 稳定对我们到底意味着什么

Swift 社区最近最重大的新闻应该就是 ABI 稳定了。这个话题虽然已经讨论了有一阵子了,但随着 Xcode 10.2 beta 的迭代和 Swift 5 的 release 被提上日程,最终 Swift ABI 稳定能做到什么程度,我们开发者能做些什么,需要做些什么,就变成了一个重要的话题。Apple 在这个月接连发布了 ABI Stability and MoreEvolving Swift On Apple Platforms After ABI Stability 两篇文章来阐述 Swift 5 发布以后 ABI 相关的内容所带来的改变。虽然原文不是很长,但是有些地方上下文没有说太清楚,可能不太容易理解。本文希望对这个话题以问答的形式进行一些总结,让大家能更明白将要发生的事情。

我是一个 app 开发者,Swift 5 发布以后会怎么样?

简单说,安装 Xcode 10.2,然后正常迁移就可以了,和以往 Swift 3 到 Swift 4 需要做的事情差不多。单论 Swift 5 这个版本,不会对你的开发造成什么影响,直到下一个版本 (比如 Swift 5.1) 之前,你几乎不需要关心 ABI 稳定这件事。关于下个 Swift 版本,我们稍后会提到这件事情。

我还是想知道什么是 ABI 稳定?

就是 binary 接口稳定,也就是在运行的时候只要是用 Swift 5 (或以上) 的编译器编译出来的 binary,就可以跑在任意的 Swift 5 (或以上) 的 runtime 上。这样,我们就不需要像以往那样在 app 里放一个 Swift runtime 了,Apple 会把它弄到 iOS 和 macOS 系统里。

所以说 app 尺寸会变小?

是的,但是这是 Apple 通过 App Thinning 帮我们完成的,不需要你操心。在提交 app 时,Apple 将会按照 iOS 系统创建不同的下载包。对于 iOS 12.2 的系统,因为它们预装了 Swift 5 的 runtime,所以不再需要 Swift 的库,它们会被从 app bundle 中删掉。对于 iOS 12.2 以下的系统,外甥打灯笼,照旧。

一个新创建的空 app,针对 iOS 12.2 打包出来压缩后的下载大小是 26KB而对 iOS 12.0 则是 2.4MB。如果你使用了很多标准库里的东西,那这个差距会更大 (因为没有用到的标准库的符号会被 strip 掉),对于一个比较有规模的 app 来说,一般可以减小 10M 左右的体积。

还有什么其他好处么?

因为系统集成了 Swift,所以大家都用同一个 Swift 了,app 启动的时候也就不需要额外加载 Swift,所以在新系统上会更快更省内存。当然啦,只是针对新系统。

另外,对于 Apple 的工程师来说,他们终于能在系统的框架里使用 Swift 了。这样一来,很多东西就不必通过 Objective-C wrap 一遍,这会让代码运行效率提高很多。虽然在 iOS 12.2 中应该还没有 Swift 编写的框架,但是我们也许能在不久的将来看到 Swift 被 Apple 自己所使用。等今年 WWDC 的消息吧。

我还想用一段时间的 Xcode 10.1,不太想这么快升级

Xcode 10.1 里的是 Swift 4.2 的编译器,出来的 binary 不是 ABI 稳定的,而且必定打包了 Swift runtime。新的系统发现 app 包中有 Swift runtime 后,就会选择不去使用系统本身的 Swift runtime。这种情况下一切保持和现在不变。旧版本的 Xcode 只有旧版本的 iOS SDK,所以自然你也没有办法用到新系统的 Swift 写的框架,系统肯定不需要在同一个进程中跑两个 Swift runtime。

简单说,你还可以一直使用 Xcode 10.1 直到 Apple 不再接受它打包的 app。不过这样的话,你不能使用新版本 Swift 的任何特性,也不能从 ABI 稳定中获得任何好处。

我升级了 Xcode 10.2,但是还想用 Swift 4 的兼容模式,会怎么样?

首先你需要弄清楚 Swift 的编译器版本语言兼容版本的区别:

编译器版本 语言兼容版本 对应的 Xcode 版本
Swift 5.0 Swift 5.0, 4.2, 4.0 Xcode 10.2
Swift 4.2 Swift 4.2, 4.0, 3.0 Xcode 10.0, Xcode 10.1
更多历史版本 …

同一个 Xcode 版本默认使用的编译器版本只有一个 (在你不更换 toolchain 的前提下),当我们在说到“使用 Xcode10.2 的 Swift 4 兼容模式”时,我们其实指的是,使用 Xcode 10.2 搭载的 Swift 5.0 版本的编译器,它提供了 4.2 的语法兼容,可以让我们不加修改地编译 Swift 4.2 的代码。即使你在 Xcode 10.2 中选择语言为 Swift 4,你所得到的二进制依然是 ABI 稳定的。ABI 和你的语言是 Swift 4 还是 Swift 5 无关,只和你的编译器版本,或者说 Xcode 版本有关。

多提一句,即使你选择了 Swift 4 的语言兼容,只要编译器版本 (当然,以及对应的标准库版本) 是 5.0 以上,你依然可以使用 Swift 5 的语法特性 (比如新增加的类型等)。

看起来 ABI 稳定很美好,那么代价呢?

Good question! 我们在第一个问题里就提到过,一切都会很美好,直到下一个版本。因为 Swift runtime 现在被放到 iOS 系统里了,所以想要升级就没那么容易了。

在 ABI 稳定之前,Swift runtime 是作为开发工具的一部分,被作为库打包到 app 中的。这样一来,在开发时,我们可以随意使用新版本 Swift 的类型或特性,因为它们的版本是开发者自己决定的。不过,当 ABI 稳定后,Swift runtime 变为了用户系统的一部分,它从开发工具,变为了运行的环境,不再由我们开发者唯一决定。比如说,对应 iOS 13 的 Swift 6 的标准库中添加了某个类型 A,但是在 iOS 12.2 这个只搭载了 Swift 5 的系统中,并没有这个类型。这意味着我们需要在使用 Swift 的时候考虑设备兼容的问题:如果你需要兼容那些搭载了旧版本 Swift 的系统,那你将无法在代码里使用新版本的 Swift runtime 特性。

这和我们一直以来适配新系统的 API 时候的情况差不多,在 Swift 5 以后,我们需要等到 deploy target 升级到对应的版本,才能开始使用对应的 Swift 特性。这意味着,我们可能会需要写一些这样的兼容代码:

top Created with Sketch.