Swift 与 Objective-C

[Swift 深入解析 #9] Swift ABI 稳定性:2019 年应用体积缩小的原因

2019 年 Swift 5 和 iOS 12.2 发布后,应用体积缩小得益于 ABI 稳定性。本文梳理 Swift 运行时从每个应用转移到操作系统的过程,以及模块稳定性和 @frozen 的权衡。

7 分钟阅读
[Swift 深入解析 #9] Swift ABI 稳定性:2019 年应用体积缩小的原因 封面图

2019 年春天,Swift 5 随 iOS 12.2 发布后,奇怪的事情发生了。什么都没做,应用体积却缩小了几 MB。 Swift ABI Stability and More

本文接续上一篇文章 Swift 深入解析 #8。

秘密就在发布说明中干巴巴的一行字里:实现 ABI 稳定性。

Swift 系列的最后一篇将讲清这件事的来龙去脉:ABI 是什么、为什么花了 5 年,以及它带来了什么可能。

这是语言历史与底层结构交汇的地方,很适合作为系列的收尾主题。

什么是 ABI — 二进制之间的约定

大家都知道 API,它是源代码层面的约定:函数叫什么、参数是什么。

ABI(Application Binary Interface)是更底层的约定,即编译后二进制之间的约定。

它规定调用函数时参数放入哪些寄存器、struct 如何布局在内存中(布局篇中见过的那些尺寸)、符号如何命名(名称修饰),以及在哪里查找元数据。

为什么这个约定很重要?因为在不同时间、使用不同编译器构建的两个二进制必须能够协同工作。

应用与操作系统内置库的关系正是如此。如果 ABI 随版本变化,用 Swift 5.0 构建的应用就无法与 Swift 5.5 运行时通信。 Swift ABI Stability and More

Swift 早期确实如此。ABI 每个版本都会变化,因此每个应用都必须完整携带所使用版本的 Swift 运行时和标准库。

每个应用都要承担几 MB 的重复负担,操作系统也无法在自己的框架中使用 Swift,因为系统框架还必须与未来的应用通信,而当时没有这种保证。

Swift 5 的 ABI 稳定性声明冻结了这一约定。调用约定、类型布局和名称修饰都已确定,后续版本会在遵守这项约定的前提下演进。 Swift ABI Stability and More

标准库随即迁入操作系统(从应用中移除后,体积便缩小)。Apple 也因此能够用 Swift 构建 SwiftUI 等系统框架。

2019 年的体积缩减,其实是这门语言走向成熟的信号。 Swift ABI Stability and More

为什么花了 5 年 — 冻结的重量

要回答“为什么不早点冻结”,必须看看冻结的代价。确定 ABI 就意味着连当时的设计错误也要永远承担。

一旦约定了布局和规范,只要已有二进制仍然存在,就无法修改。

回想一下 Swift 1~4 时期有多么动荡。语法几乎每个版本都被重做。如果当时冻结,今天的 Swift 就会被困在早期设计的牢笼中。 Swift ABI Stability and More

我们一直等到泛型实现稳定、值类型布局策略得到验证,也等 Evolution 流程(哲学篇第 4 集)解决重大设计争论,前后用了 5 年。

看看 C++ 因数十年的 ABI 惯性而拒绝改进方案的案例,就能理解这种谨慎的价值。

每个应用背负的 Swift 运行时背包迁移到操作系统共享支柱的前后对比
每个应用背负的运行时背包已经迁移到操作系统共享支柱

第二个约定 — 模块稳定性与库演进

不过,ABI 稳定性仍留下一个问题:二进制框架的分发。

Swift 5.0 之前的编译器使用名为 swiftmodule 的二进制格式交换模块接口,但它与编译器版本绑定。 Swift ABI Stability and More

Xcode 一升级,所有已分发的 SDK 就会全部失效。

Swift 5.1 的模块稳定性解决了这个问题。它引入了基于文本的稳定接口文件(.swiftinterface),从而可以读取使用其他版本编译器构建的框架。 Swift ABI Stability and More

基于 XCFramework 分发 SDK 的生态(支付、分析和地图 SDK)就建立在此基础之上。

与之配套的概念是库演进模式。它提供一个选项,使框架即使之后新增存储属性,也不会破坏现有应用,但代价很有意思。

该模式下的 public struct 会成为布局未确定的 resilient type。客户端无法在编译时知道它的大小,并要承担间接访问的成本。

这正是布局篇中“知道大小就更快”的反命题。

因此,演进模式提供了一个名为 @frozen 的出口:“冻结这个类型的布局,但允许直接访问。”标准库中的 Int 和 Optional 是 @frozen,原因就在这里。

对应用开发者来说,实际意义如下:应用 target 和源码分发包(大多数 SwiftPM 包)不需要此模式,开启它反而会吃亏。

只有以二进制形式分发 SDK 的一方才需要开启。问题“BUILD_LIBRARY_FOR_DISTRIBUTION 是什么?”的答案就在这一段。

收尾 — 贯穿整个系列的一个故事

ABI 成为 Swift 系列的最后一篇是有原因的。到目前为止介绍的概念都在这里汇合。

值类型的布局(值篇、布局篇)被冻结后,struct 才能跨越二进制边界。witness table(分派篇)的格式确定后,协议才成为框架 API。

而 Evolution 流程(哲学篇第 4 集)让设计走向成熟,也让我们有信心将其冻结。

通过类型保证安全(可选值、ARC、Sendable),明确成本(unsafe、any、@unchecked),并将复杂性分成阶梯(Progressive Disclosure)。

这些原则从语法层一直贯穿到二进制层并保持一致,这就是整个系列想表达的故事。

Swift 现在仍在 Evolution 论坛上构建下一层阶梯。希望这个系列能成为你自行读懂这些变化的地图,我们就此收尾。

对比可演进框架与 @frozen 冻结结构访问速度的插图
是演进的灵活性,还是冻结的速度?@frozen 就是那个开关

总结

  • ABI 是二进制之间的约定(调用约定、布局和名称修饰),并在 Swift 5(2019 年)中被冻结。因此,标准库迁入操作系统,应用体积缩小,Apple 也能够用 Swift 构建 SwiftUI。
  • 冻结花了 5 年,因为这意味着要永远承担设计错误。这段时间用于等待泛型、布局和 Evolution 流程成熟。
  • Swift 5.1 的模块稳定性(.swiftinterface)开启了基于 XCFramework 的二进制框架生态。
  • 库演进模式以间接访问成本为代价,换取框架演进时应用仍不崩溃,而 @frozen 就是这个出口。对于应用 target,这是不必要的选项。
  • 系列结论:Swift 关于类型化安全、明确成本和分阶复杂性的原则,从语法到二进制始终一致。 Swift ABI Stability and More

来源与核验标准

  • Swift ABI Stability and More — Swift.org · 官方发布 · 核验日期 2026-08-17 · 依据:Swift 5 ABI 稳定性、操作系统搭载运行时以及应用体积变化

延伸阅读

Swift 深入解析系列