WWDC 22——Link Fast — 加速编译和App启动

1,480 阅读4分钟

这篇文章主要是对 WWDC 22 的视频 Link Fast——Improve build and launch times 进行归纳总结。

视频里聊到了,静态库和动态库的一些链接细节,以及 Xcode 14 为我们提供了哪些工具,来对我们的编译时长,做一些可能性的优化。

苹果在 Xcode 14 发布了他们开发的最新的静态库链接器——ld64,苹果聊到了4种新的链接器配置,用来为开发者的编译加速——但苹果也说,具体有没有用,还得你亲自在工程里试了,才知道。:)这很苹果。

静态库的选择性链接——通过-all_load和-dead_strip规避

默认的情况下,链接器是按函数的调用顺序进行链接的,这意味着,整个链接是单线程的——因为只有在a调用了b的情况下,链接器才会去链接b。 

但如果不需要链接器去进行这个判断,就可以让整个链接过程并发。

可以通过在 other linker flags 中添加-all_load参数,来告诉链接器,全量链接静态库的符号,不需要选择性链接,从而加速链接的过程。

-all_load 也有缺点,因为可能会把不在使用的代码也加载进来,导致包体积变大。

但也可以通过添加 -dead_strip 链接器选项,来将无法访问的代码和数据移除。

何时使用 静态库?

  • 任何代码变更,都会导致静态库重编,所以用于封装成静态库的代码,最好是比较稳定的代码。
  • 选择性加载是连接器的历史行为,并且会拖慢链接过程,且导致链接只能顺序发生,而不能并行发生——如果工程里确实不需要,可以添加-all_load 链接标记,苹果最新的静态库链接器 ld64 能借此加速链接过程。

-no_exported_symbols 选项

通常来说主程序不需要导出,这一步意味着编译器在编译时会生成并导出一个前缀树型结构的符号文件,后续可能被用到——如果工程有插件、或关联了XCTest的话,最好不开启该选项。

但这个导出数据,如果量不大的话,其实对于链接的时间的影响,非常有限。

但也可以用图中的命令看看,如果导出数量很大,但该结果没用上,确实可以考虑不导出。

dyld_info -exports /path/to/binary | wc -l

-no_deduplicated

取消去重,可以在debug模式中使用,看看是不是能加快编译。


动态库方面

使用动态库可以加快编译,但启动速度会变慢。

动态库的链接原理是,编译过程中,不需要将主项目中对动态库中的接口的调用,链接到主项目中,而是在主项目中,留下了类似协议的调用。直到启动APP,才将真正的实现,关联起来——也就是 dyld 过程。

官方对动态库的态度:

为了启动更快——少点用动态库:)。

但静态库太多的话,确实会拖慢编译速度,而动态库有助于这一点。

重点是找到一个平衡点——静态库用多少,动态库用多少。

最低运行版本调到 13.4 以上,App 的启动在 iOS 16 会得到加速

因为苹果在 iOS 13.4 以上的设备部署了动态库链式修复(dylib Chain Fixups)的功能,如果将部署最低版本设到 13.4 以上,启动时的动态库加载过程会得到加速。

一些用来查看启动时 dyld 过程细节的命令行工具

  • dyld_usage

  • dyld_info

查看Dyld 信息


总结

  • 回顾 app 中对静态库,动态库的使用情况,并考虑它们对编译时长,以及启动的影响;
  • 尝试使用 Xcode 14 编译项目;
  • 尝试前面提到的4个编译选项,看看对编译时长,是否有真实的优化效果——从视频里的意思,使用在静态库,或者主工程上较为合适(笔者在一个大小适中的项目上进行了尝试,结果体现到的优化比较有限——:)这又很苹果——也可能每个项目情况都不同,还请读者自行尝试哈~);
  • 将 iOS 最低部署版本设置到 13.4 或更高,然后在 iOS 16 上看看 App 的首次启动时长,是不是有所优化。