症状: Xcode 27 编译变慢,构建、索引和 CI 等待时间同时拉长。
最快解法: 先记录一次冷构建和连续增量构建的 Timing Summary,再按缓存失效、脚本依赖、索引竞争、磁盘与内存压力逐层排查,不要先清空全部 DerivedData 或直接升级硬件。
截至 2026 年 8 月 24 日,官方发布说明页面仍将 Xcode 27 列为 Beta,并显示当前可用的 Beta 版本信息可能继续变化。生产链路如果只在 Xcode 27 Beta 上出现退化,应保留 Xcode 26.6 作为稳定基线,并把新版本放到隔离节点中双轨验证,而不是让所有 CI 任务一起切换。查看官方 Xcode 发布说明
谁适合按这套方法排查
这篇文章适合升级 Xcode 27 后发现本地或远程构建时间明显变化的 Apple 平台开发者,也适合维护长期在线远程 Mac、需要判断是否扩容的 DevOps 与研发平台工程师。
如果你负责生产 CI,同时还要验证 Beta 工具链的兼容性,本文会帮助你把“感觉变慢”拆成可以复测的工程证据。
⚠️ Xcode 27 当前版本状态属于时效信息。后续 Beta、RC 或正式版可能改变已知问题与修复状态,发布前应重新核对官方 Release Notes,而不是沿用本文当前日期的判断。
先把“变慢”拆成可比较的构建任务
很多团队把首次冷构建、日常增量构建、Archive、测试和编辑器索引混在一起比较,最后得到一个没有决策价值的平均耗时。你需要固定项目提交、Scheme、目标平台、构建配置和节点,然后分别保存以下证据:
| 任务类型 | 主要观察对象 | 能回答的问题 | 下一步判断 |
|---|---|---|---|
| 冷构建 | preparation、编译、链接、脚本、依赖解析 | 第一次完整准备是否异常 | 关注依赖、脚本、模块与磁盘 |
| 连续增量构建 | 未修改目标是否重复执行 | 缓存是否被正确复用 | 关注依赖图、输入输出和配置变化 |
| Archive | Release 配置、签名、脚本、资源处理 | 发布链路是否单独变慢 | 不要用 Debug 结果替代 |
| 测试构建 | 测试目标、模拟器、并发任务 | 测试环境是否抢占节点 | 分离 Simulator 与命令行任务 |
| 编辑器索引 | SourceKit、索引、Preview、磁盘 I/O | IDE 卡顿是否等同于编译慢 | 单独判断交互负载 |
在图形界面中选择 Product > Perform Action > Build With Timing Summary,在报告导航器里查看任务级耗时;命令行则使用 xcodebuild -showBuildTimingSummary。官方建议先收集每个构建任务的时间,再决定最值得优化的部分。查看增量构建计时方法
为了让结果可复现,你至少要保存:
- 当前 Git 提交和工作区是否干净;
- Xcode 版本、Scheme、Destination 与构建配置;
- 冷构建前是否重启节点;
- 连续增量构建之间只修改了哪些文件;
- 构建时是否同时运行索引、Preview、Simulator 或其他 CI 任务。
如果同一项目在 Xcode 26.6 和 Xcode 27 上的差异无法在相同节点、相同提交与相同配置下复现,就不能把问题直接归因于 Xcode 27。
增量构建失效:为什么每次修改都像全量编译
“只改一行代码,却重新编译多个目标”通常说明构建系统没有识别出稳定的依赖关系,或者项目设置持续改变了输入条件。官方构建系统会尽量并行执行没有依赖关系的任务,但错误或过宽的 Target 依赖会迫使任务串行执行。查看构建系统与目标依赖说明
先在 Timing Summary 和详细构建日志里确认以下现象:
- 未修改的 Target 是否在每次构建中重新出现;
CompileSwift、CompileC或模块构建是否覆盖了大量未改文件;- preparation 阶段是否反复生成依赖图;
- 不同配置是否改变了预处理宏、搜索路径或模块选项;
- DerivedData 是否因为路径变化、工作目录变化或任务隔离策略而无法复用。
如果一个工程包含多个 App、Framework、Extension 或测试目标,检查 Build Phases > Target Dependencies。依赖关系过多会减少并行机会;依赖关系遗漏则可能导致脚本生成文件后,其他目标仍无法正确判断构建顺序。目标依赖应描述真实的生成关系,而不是为了“保险”把所有目标互相连接。
模块配置也可能造成重复工作。相同依赖如果使用不同的编译选项,构建系统可能生成多个模块变体;Timing Summary 能帮助你观察模块是否因为配置差异被重复构建。查看显式模块依赖的诊断说明
DerivedData 应该什么时候清理
删除 DerivedData 不是测量方法,也不是默认修复。你只有在日志显示模块缓存、构建产物或索引数据异常,或者修改构建设置后旧缓存无法合理复用时,才应清理对应项目的数据。
建议采用局部清理:
- 记录清理前的冷构建和增量构建 Timing Summary;
- 关闭 Xcode,确认没有相关构建进程继续运行;
- 只删除当前项目对应的 DerivedData 目录;
- 重新执行一次冷构建;
- 再连续执行增量构建,确认缓存是否恢复复用。
如果清理后只有第一次构建变慢,后续增量构建恢复正常,说明缓存重建确实影响了结果;如果连续增量构建仍然重复编译,根因更可能在依赖图、脚本或配置变化,而不是文件夹本身。
依赖解析与 Run Script 让等待时间被隐藏
依赖阶段经常被误认为“编译器变慢”。实际上,Swift Package、CocoaPods、私有仓库访问、生成代码和资源处理都可能占用大量等待时间,尤其是在远程 Mac 作为 CI 节点时,网络路径和凭据状态会直接影响构建准备阶段。
先对比一次冷构建和后续构建的日志:
- 每次构建是否重新解析 Swift Package;
- 是否重复下载或生成依赖文件;
Package.resolved是否已提交并与当前提交一致;- 私有依赖是否因为权限或 SSH 凭据问题反复重试;
- 远程节点访问代码仓库或制品服务时是否出现网络等待。
在 CI 中,应固定 Package.resolved,并根据流程需要使用 -disableAutomaticPackageResolution,避免构建时自动改变依赖版本。官方文档还特别强调,依赖版本文件应提交到代码仓库,以保持 CI 使用预期版本。查看 Swift Package CI 依赖建议
Run Script Phase 是另一个高频原因。官方说明指出:如果脚本没有声明输入文件或输出文件,Xcode 默认会在每个构建周期运行它,包括增量构建;没有明确输入输出时,构建系统也无法判断脚本是否可以跳过。查看 Run Script 输入输出配置
你可以按下面的顺序修复:
- 列出脚本实际读取的输入文件;
- 列出脚本实际生成的输出文件;
- 在 Run Script Phase 中填写 Input Files、Output Files,或使用
.xcfilelist; - 删除脚本里的无条件全量扫描和重复生成动作;
- 连续执行多次增量构建,确认脚本在输入未变化时被跳过;
- 如果脚本访问远程仓库或制品服务,单独记录网络等待,而不是把它算进编译器耗时。
若脚本没有自然输出文件,可以生成一个稳定的标记文件供构建系统判断;但这个标记文件必须真正反映脚本是否完成,不能为了让日志“看起来更快”而伪造依赖关系。
索引、Preview 与 Simulator 不应和 CI 抢同一份资源
远程 Mac 上的图形会话可能同时运行 Xcode 索引、SourceKit、SwiftUI Preview、Simulator 和命令行构建。此时你看到的“编译变慢”,有可能只是多个任务争抢 CPU、内存和磁盘 I/O,而不是单个构建任务本身退化。
建议做一次隔离复测:
- 在纯 SSH 命令行环境执行同一构建;
- 关闭 Preview 和不需要的 Simulator;
- 暂停无关的索引或大型工作区扫描;
- 记录命令行构建与图形会话构建的 Timing Summary;
- 比较两次构建时的内存压力、交换活动与磁盘写入。
如果命令行构建稳定、图形会话明显变慢,优先处理并发任务和索引范围;如果两种方式都在同一阶段变慢,再回到依赖、脚本或节点资源检查。
不建议长期关闭索引来掩盖问题。短时间关闭索引可以帮助隔离变量,但它会影响代码导航、跳转和错误提示,不能作为生产开发环境的长期优化方案。
远程 Mac 节点资源:什么时候才值得扩容
判断配置不足,不能只看 CPU 使用率。你需要同时观察内存压力、Swap Used、磁盘剩余空间、构建目录 I/O,以及单任务与并发任务的差异。系统监控文档明确说明,内存压力会综合空闲内存、压缩内存、交换速率和缓存文件等因素;只看“还有多少空闲内存”容易得出错误结论。查看内存压力与交换活动说明
可按下面的标准判断:
- 构建阶段耗时正常,但并发任务时内存压力持续变黄或变红:优先减少并发,随后评估更大内存配置;
- Swap Used 在构建期间持续增长,且磁盘 I/O 同步升高:说明内存不足或缓存布局不合理;
- 单任务正常、并发任务明显退化:先检查任务隔离、工作目录和缓存共享,不要马上归因于芯片;
- 冷构建和增量构建都在依赖准备阶段等待:优先检查网络、凭据与依赖缓存;
- 磁盘空间不足或构建目录频繁清理:先调整缓存保留策略和目录布局,再考虑节点扩容。
共享远程 Mac 时,不要让多个项目无条件共用同一个 DerivedData、包缓存和临时目录。错误共享可能造成缓存互相覆盖,也可能为了避免污染而频繁重建,最终两种方式都会拖慢 CI。
如果你需要长期在线运行多个构建任务,可以参考本站的远程 Mac 磁盘缓存和长期运行验收思路,把磁盘、内存、重启后缓存恢复和并发任务作为同一套验收条件,而不是只验收一次成功构建。
一份可以直接执行的排障清单
- [ ] 固定同一项目提交、Scheme、目标平台和构建配置;
- [ ] 分别保存一次冷构建与连续增量构建的 Build Timing Summary;
- [ ] 使用
xcodebuild -showBuildTimingSummary复核命令行结果; - [ ] 检查未修改 Target 是否被重复编译;
- [ ] 检查 Target Dependencies 是否过宽、遗漏或存在不必要的串行关系;
- [ ] 检查 Run Script Phase 是否声明了真实输入文件和输出文件;
- [ ] 对比 Swift Package 或其他依赖的首次解析与后续构建日志;
- [ ] 确认
Package.resolved与当前提交一致,并检查私有依赖访问权限; - [ ] 分离 Xcode 索引、Preview、Simulator 与命令行构建;
- [ ] 记录内存压力、Swap Used、磁盘 I/O 和可用空间;
- [ ] 对共享节点分别测试单任务与并发任务;
- [ ] 只有在日志指向缓存异常时,才清理对应项目的 DerivedData;
- [ ] 若只在 Xcode 27 Beta 复现,保留 Xcode 26.6 生产链路;
- [ ] 用冷构建、连续增量构建和重启后复测形成最终验收记录。
常见问题
为什么修改一行代码却像重新编译整个项目
先检查构建日志,而不是先删除 DerivedData。若未修改的 Target、模块或脚本反复出现,重点核对目标依赖、脚本输入输出、预处理宏、搜索路径和其他条件化配置。只有当这些条件稳定后,缓存才有机会被正确复用。
远程 Mac 上索引一直不结束怎么办
把索引问题与构建问题分开。先关闭 Preview、Simulator 和不必要的并发任务,再执行一次纯命令行构建;如果命令行正常而编辑器仍卡顿,说明应检查索引范围、内存压力和磁盘 I/O,而不是直接更换节点。
删除 DerivedData 能不能解决编译慢
可以解决部分缓存损坏或旧模块异常,但不能修复错误的依赖图、无条件运行的脚本和反复解析的依赖。清理前保存基线,清理后必须完成一次冷构建和多次增量构建,否则你无法知道问题是否真正消失。
怎么区分项目问题和 Mac 配置不足
项目问题通常会稳定集中在 preparation、依赖解析、脚本或重复编译阶段;配置不足则更常表现为并发时内存压力、交换活动或磁盘 I/O 持续饱和。使用同一提交在单任务、并发任务和重启后分别测试,才能避免凭主观感受扩容。
修复、回退与扩容的决策边界
如果 Timing Summary 显示时间主要花在重复编译、依赖解析或 Run Script Phase,先修项目和构建配置;这类问题即使换成更强的 Mac,也可能只是把错误流程执行得更快,无法消除重复工作。
如果同一项目、同一提交只在 Xcode 27 Beta 节点退化,生产 CI 应继续使用 Xcode 26.6,同时为 Xcode 27 保留独立节点,采用双轨方式观察后续版本变化。本站已有多版本 Xcode 共存与命令行切换的相关实践,可用于补充工具链隔离与回退准备。
只有在构建阶段本身没有异常,但节点在单任务或并发任务下持续出现内存压力、Swap Used 增长或磁盘 I/O 饱和时,才进入配置扩容评估。扩容前应先完成至少一次重启后复测,并确认问题不会随着缓存重建、依赖重新解析或后台索引消失。
当前方案与远程 Mac 方案如何取舍
如果你继续把所有版本装在同一台本地 Mac 上,常见缺点是稳定版与 Beta 工具链互相干扰、机器无法长期在线执行 CI,而且本地磁盘和内存资源会被开发环境、Simulator 与构建缓存共同占用。使用普通 Linux 云主机也无法直接替代需要 Xcode、签名工具链和 macOS 构建环境的任务。
当你需要同时保留 Xcode 26.6 与 Xcode 27、复制同一项目基线,或验证远程节点是否真的需要扩容时,独立的远程 Mac 更适合做隔离变量。你可以通过 SSH、VNC 或网页控制台复测完整图形与命令行环境,再根据证据决定回退、修项目还是升级配置;如果只是临时验证或阶段性 CI 负载,可先查看 KVMFLUX 的远程 Mac 方案,避免为了一个仍在变化的 Beta 工具链提前购买长期硬件。
常见问题 FAQ
为什么我只改了一行代码,Xcode 27 却像重新编译整个项目?
通常不是单纯的编译器速度问题,而是增量构建条件失效。重点检查 Target 依赖、脚本输入输出、条件化 Build Settings、模块配置和 DerivedData 路径。先用 Build Timing Summary 确认哪些未修改目标被重新执行,再决定是否清理项目缓存。
远程 Mac 上的 Xcode 索引一直不结束,应该先做什么?
先把图形界面负载与命令行构建分开测试:关闭不必要的 Simulator、Preview 和并发任务,记录索引期间的内存压力、交换活动与磁盘 I/O。如果命令行构建正常而编辑器卡顿,问题更可能是索引资源竞争,而不是构建节点算力不足。
删除 DerivedData 能不能真正解决 Xcode 编译慢?
只有在构建日志指向缓存损坏、模块缓存异常或旧产物无法复用时,清理对应项目的 DerivedData 才有意义。删除全部 DerivedData 会抹掉可复用缓存,首次构建反而更慢,因此不应把它当成默认修复动作。
怎么判断构建慢是项目问题,还是远程 Mac 配置不足?
用同一提交、同一 Scheme 和同一构建配置做冷构建、连续增量构建及重启后复测。如果耗时集中在脚本、依赖解析或重复编译,就是项目问题;如果构建阶段正常但内存压力、交换或磁盘 I/O 持续饱和,才有理由评估节点扩容。
让编译速度回到可控:几分钟开通 KVMFLUX 专属云端 Mac
租用一台独享的物理 Mac mini M4,避免共享节点排队与资源争抢,为构建和索引提供稳定性能。 通过 SSH 接入自动化流水线,或使用 VNC 远程排查图形界面、模拟器与签名问题。 按日、周、月或季灵活租用,按需增加存储空间,缓存、依赖和多个开发环境都能持久保留。 选择新加坡、日本、韩国、香港或美国节点,付款后几分钟内收到连接凭证,立即扩容你的 macOS 构建能力。