← 所有文章

Apple 的字体解释器现已改用 Swift,速度提升 13%

Apple 安全团队发布了一份足以平息争论的成果。TrueType hinting 解释器——一个自 1991 年以来就在 Apple 平台上解析不受信任字体数据的字节码解释器——已经从 C 重写为内存安全的 Swift,而且”平均而言,我们的 Swift 解释器比它所取代的 C 解释器快 13%”。1 这篇文章由 Apple 安全团队中专注于 Swift 采用工作的 Scott Perry 撰写,源代码已作为参考实现发布在 GitHub 上,而正确性的标准不是”通过测试”,而是与 C 原版渲染出逐像素一致的结果。1 内存安全、比 C 更快、并且自 2025 年秋季发布以来已在生产环境中运行:这篇文章是迄今为止对”内存安全会牺牲性能”这一说法最有力的实证回应。

TL;DR

  • Apple 之所以将 TrueType hinting 解释器从 C 重写为 Swift,是因为字体解析器是安全攸关的攻击面:解释器运行字体内嵌的字节码,其特征是”由输入驱动的控制流、复杂的数据结构以及需要精细处理的内存管理——这正是那种难以做到完美、且内存错误更容易被利用的代码”。1
  • 这一成果已随 2025 年秋季的版本发布,”自启用以来未收到任何针对它的缺陷报告”,平均比 C 实现快 13%。1
  • 性能提升来自一些具体且可移植的技术:使用 ~Copyable~Escapable 值类型以消除引用计数和独占性开销,使用 Span 实现安全的序列访问,用投影类型替代 C 结构体从而消除了约占运行时 20% 的拷贝开销,以及保持泛型可特化。1
  • 验证工作是低调的杰作:一套在两种实现上都达到 99.7% 覆盖率的单元测试,一个经模糊测试精简后从 1000 万个 PDF 缩减到 4,200 份文档、内嵌 25,572 个字体的语料库,2700 万个字形分别在四种变换下渲染并逐位图比对,以及几乎是解释器代码四倍量的测试代码。1
  • 解释器源代码以 MIT 许可证发布在 apple/truetype-hinting-interpreter-example,是作为参考实现的生产级代码。2

字体解释器为何是一个安全话题

TrueType 字体可以包含程序。Apple 在 20 世纪 80 年代末创造的这种格式自带一个围绕专用字节码解释器构建的 hinting 引擎,旨在让轮廓在低分辨率显示器上忠实地光栅化。1 当 TrueType 在 1994 年可内嵌于 PDF、并在 2008 年可内嵌于网页之后,这个解释器就开始执行来自”互联网各处不受信任字体”的程序。1 凡是关注过 iOS 安全史的人都知道接下来会怎样:字体解析器承载了该平台一些最著名的漏洞利用链,而文章无需重提历史,就点明了结构性原因。由输入驱动的控制流加上手动内存管理,正是内存错误滋生之处。

团队面临的约束使这次重写比从零开始的移植更难。二进制兼容意味着现有程序”必须继续像以前一样运作,对新实现已经就位这件事浑然不觉”,而由于 hinting 会彻底改变字形在屏幕上的呈现方式,正确性被定义为”与 C 实现的输出完全兼容”。1 是逐像素一致,而非大致正确。

验证方法论才是真正的匠心所在

在进行性能优化之前,团队先搭建了证明所需的工具。一套单元测试同时针对 C 和 Swift 实现,覆盖详尽,两者均达 99.7%,并随开源版本一同发布。1 为获得贴近真实世界的覆盖,他们用模糊测试器将一个由 1000 万个 PDF 文件组成的语料库精简到 4,200 份,且代码覆盖率毫无损失;这些文档内嵌了 25,572 个字体,其 2700 万个字形分别在四种变换下渲染,所得位图再与参考解释器进行比对。1 到最后,团队编写的测试代码行数几乎是解释器代码的四倍。1

这个比例正是值得内化于心的部分。重写之所以能大刀阔斧地进行,是因为每一项优化都对照着一个预言机运行,它逐个位图地回答行为是否发生了改变。文章恰恰归功于这个循环:详尽的覆盖率和定义良好的内部边界”让重构变得容易得多,进而加速了’度量—修复’的优化循环,同时把引入缺陷的风险降到最低”。1

这 13% 从何而来

文章将性能优化工作分为四类,每一类都有一条可移植的经验。1

用不可拷贝类型消除引用计数和独占性检查。 Swift 的 ARC 和运行时独占性检查会带来开销,而别名会使其更严重,且解释器的规范本身就内含了一定量无法消除的别名。修复方法是架构层面的:全程采用 ~Copyable 值类型,把引用类型留给高层抽象,并使用 Swift 6.2 引入、可一直回溯部署到 macOS 10.14.4 和 iOS 12.2 的 Span 来提供对序列的高效访问。1

消除语言边界处的拷贝。 C 代码将字形点存储为一个包含八个数组的结构体,对缓存友好但不符合 Swift 风格。最初的桥接代码会把这些数据拷贝进 Swift 再拷贝回来,而这些拷贝消耗了新解释器约 20% 的运行时。1 替代方案是投影类型:它包裹 C 结构,在不进行拷贝的情况下中转边界安全的访问,遵循 WebKit 的 Safer Swift Guidelines——一个持有指向 C 元素 Ref@safe 不可拷贝、不可逃逸结构体,每一处不安全表达式都附带一条 // SAFETY: 注释,记录其成立所依据的不变量。1

省去短生命周期的分配。filtermap 这样的操作会进行分配,而只有当值发生逃逸时这种分配才有意义。解释器的栈弹出操作从一个会分配被弹出元素数组的直观版本,演化为一个续延传递(continuation-passing)的版本:调用方传入一个闭包,在元素被移除之前对这些元素的 borrowing Span 进行操作,编译期的独占性检查保证栈无法从闭包内部被修改。1 没有堆分配,没有元素拷贝,而且安全论证是结构性的,而非依赖约定。

保持泛型可特化。 来自协议和泛型的动态派发通常可以被优化掉,但并非无条件如此。团队给出的性能剖析指引是:”如果你在热点路径中看到未特化的泛型或协议见证表,那就说明优化器没有获得足够的可见性”,此时该实现可能会从内联中受益。1

最终的结果并没有以可读性换取速度。文章的论点是,Swift 的类型系统催生了一些抽象——定点数值类型、自带转换的栈元素、以及那些投影类型——这些抽象在启用优化构建后”在大幅提升可读性的同时实现了零成本”。1 团队对适用范围也直言不讳:解释器的内部状态全部是不可拷贝结构,但顶层类型仍是一个由 Objective-C++ 调用的 @objc 类,因为”热点路径快,冷路径方便”。1

低调的智能体视角

文章接近结尾处有一段,其重要性远超它所处的位置所暗示的。在完成迁移后,团队”把我们学到的东西提炼成了给 LLM 编程助手的指令,此后已在其他项目中成功使用”,LLM 提升了团队将 C 和 C++ 转换为 Swift 的效率。1 Apple 安全团队正把迁移经验编码为智能体指令,并在多个代码库间复用——这正是 Apple 在本周期产品化为可导出智能体技能的同一模式,详见 Xcode 27 的智能体技能导出。一次手工完成的迁移变成了一份操作手册;这份手册又成为接下来十次迁移的杠杆。

从中可以汲取什么

文章给出了三个曾经存疑、如今已成定论的论断。内存安全的 Swift 能够在最热的热点路径——一个字节码解释器中——取代安全攸关的 C。这种替换可以更快,靠的不是魔法,而是不可拷贝类型、拷贝消除以及对特化友好的设计。而且只要投入足够的测试,这次迁移可以被验证为逐像素等价,团队为此投入的测试量约为实现代码的 4 倍。对于任何手握会接触不受信任输入的 C 或 C++ 解析代码的人来说,这篇文章、那个开源仓库,以及 Swift 6.4 中细粒度的严格内存安全选择性启用 三者结合在一起,让”我们总有一天会迁移”这种说法比上周更加站不住脚。

常见问题

Apple 究竟重写了什么?

是 TrueType hinting 解释器:那个执行字体内嵌 hinting 程序、让字形轮廓得以良好光栅化的字节码解释器,自 1991 年 System 7 起就是字体技术栈的一部分。1 它在 2025 年秋季的版本中从 C 改写为内存安全的 Swift,渲染输出与 C 实现逐像素一致。1

Swift 在这里真的比 C 快吗?

平均而言,是的:比它所取代的 C 解释器快 13%,以每字形所耗的 CPU 兆周期为度量,覆盖了 macOS 上随系统发布的所有带 hinting 字体以及一批非系统字体样本。1 这些提升来自通过不可拷贝类型消除引用计数、用投影类型去除跨语言拷贝、省去短生命周期的分配,以及保持泛型可特化。1

我能看到代码吗?

可以。Apple 以 MIT 许可证将该解释器发布在 apple/truetype-hinting-interpreter-example,并将其描述为作为参考实现的生产级代码,而非一个持续维护的开源项目。2 单元测试套件也随之一同发布。1

字体解释器为何对安全很重要?

因为它执行来自不受信任输入的程序。字体会从任何地方随网页和 PDF 而来,而解释器所兼具的由输入驱动的控制流与需要精细处理的内存管理,正是内存破坏类漏洞历来滋生的地方。1 将这一攻击面迁移到内存安全的语言可以消除整整一类缺陷,而文章也报告称自 Swift 实现启用以来未出现任何缺陷。1


这次迁移印证了 Swift 工具链整个月以来所传递的方向:《Swift 新特性(2026)》 中细粒度的内存安全诊断、Swift 6.2 并发实践 中默认即高性能的并发叙事,以及 Apple 在 其针对提示注入的第一方答案 中正式记录在案的安全立场。整个系列的中枢页面是 Apple 生态系列

参考资料


  1. Scott Perry,Swift at Apple: Migrating the TrueType Hinting Interpreter,Swift.org 博客,2026 年 6 月 12 日。本文关于作者身份(Apple 安全团队,专注于 Swift 采用)、安全立场(字体解析器处理不受信任的数据;”由输入驱动的控制流、复杂的数据结构以及需要精细处理的内存管理——这正是那种难以做到完美、且内存错误更容易被利用的代码”)、TrueType 历史(由 Apple 于 20 世纪 80 年代末开发,1991 年随 System 7 发布,1994 年可内嵌于 PDF、2008 年可内嵌于网页)、2025 年秋季发布日期、以每字形 CPU 兆周期度量的 13% 平均性能提升、启用以来无缺陷的表述、二进制兼容与逐像素一致的正确性要求、验证方法论(两种实现均达 99.7% 覆盖率、经模糊测试将 1000 万个 PDF 语料库精简至 4,200 份文档、25,572 个内嵌字体、四种变换下的 2700 万个字形、近 4 倍的测试代码)、四类优化(~Copyable 值类型与可回溯部署到 macOS 10.14.4 和 iOS 12.2 的 Span;在拷贝消耗约 20% 运行时后改用投影类型替代 C 结构体,遵循 WebKit 的 Safer Swift Guidelines 并使用 @safe// SAFETY: 注释;基于 borrowing Span 的续延传递式栈弹出;特化与内联指引)、零成本抽象的论点、@objc 顶层边界(”热点路径快,冷路径方便”),以及从此次迁移中提炼并在其他 C/C++ 转 Swift 项目中复用的 LLM 助手指令等内容的来源。 

  2. Apple,truetype-hinting-interpreter-example,GitHub,MIT 许可证。本文关于该仓库的存在、许可证,以及其作为 Swift TrueType 解释器、以参考实现为目的的生产级代码这一描述的来源。 

相关文章

UIKit 的场景强制要求:哪些应用在 iOS 27 上将无法启动

使用 iOS 27 SDK 构建的应用必须采用 UIKit 基于场景的生命周期,否则将无法启动。本文梳理时间线、迁移步骤以及对应的 agent skill。

3 分钟阅读

Container machine:在 Mac 上运行持久化的 Linux 环境

Apple 的 container 工具迎来 1.0,带来 container machine:一个快速、持久的 macOS 上 Linux 环境,并挂载了您的用户、主目录和 dotfiles。

4 分钟阅读

从76到100:实现Lighthouse满分

一个个人作品集网站如何从移动端Lighthouse性能评分76分、CLS为0.493,提升到所有类别均达到100/100/100/100的满分。

3 分钟阅读