[ WWDC2018 ] - 使用Xcode做更快的构建 Building Faster in Xcode

使用新的Xcode 10,在构建项目时,性能和开发者的效率有了很大提升。对于大型项目,此前Xcode构建过程很漫长,这种情况下开发人员可能会分心并抱怨,但苹果已经做了一些努力来优化构建时间,尤其是Swift语言。一般来说构建时间与项目代码行数,依赖性以及用于构建的机器配置等等多个因素相关。这次发布的Xcode 10和Swift编译器有多种新功能,使Swift的构建比以往更快。

1.了解Xcode的Targets和依赖以及构建过程

Xcode构建过程是一系列使用clang,ld,actool,ibtool等本地命令行工具进行的任务集合。它涉及编译和链接源代码,拷贝资源(如资源,StoryBoards,代码)使用构建脚本以及其他方式签名和定制内容。一般来说,Xcode构建过程能够以正确的顺序有效地自动完成这些任务,以便保证构建过程可靠。在Xcode9~10下,如果可能的话,源文件的编译可以并行进行,这样链接器可以一次执行所有这些任务来准备应用程序的可执行文件。

1 (1).png

Xcode构建系统会自动分析目标之间的依赖关系,并确定哪些任务可以并行进行。

2 (1).png

3 (1).png

Xcode项目由多个目标组成,构建时一个目标可能依赖另一个或多个目标。
Xcode目标需要指定构建的产品,例如我们可能针对主应用程序、单元测试或UI测试有不同的目标。
当一个目标依赖另一个目标来构建,然后它创建依赖性,例如对iOS应用程序进行单元测试时,我们首先需要构建主应用程序,然后我们可以构建单元测试目标来执行单元测试。 在Xcode构建阶段中,我们可以使用目标依赖关系明确指定目标依赖关系,并隐式使用链接二进制库。

另外了解增量构建(Incremental Builds)的概念也很重要:如果我们不改变它,我们就不需要构建所有的东西。

通过观看这段 WWDC Session 408,我们会更多地了解Xcode背后的构建过程。为了加快构建过程,我们应该了解并尝试修改构建系统,通过识别和优化应用程序中的依赖关系来更快地完成工作。这就是为什么了解Xcode构建过程在底层如何工作很重要。

2.了解并行构建

如上面所述,Xcode构建系统会自动分析目标之间的依赖关系,并确定哪些任务可以并行进行。 一般来说构建的时间如下面所示。 4 (1).png

最简单时顺序构建目标需要时间,但没有完全利用多核系统资源。如果可能的话,构建目标将会非常慢。使用Xcode 10,我们可以并行化构建并分析项目模块间的依赖关系,从而显着减少构建时间。我们可以通过编辑Xcode Scheme并在方案的构建操作中检查并行构建来启用并行构建。参见如下选项 5 (1).png

6 (1).png

在项目设置中确保选择了Parallelize Build和Find Implicit Dependencies,以确保我们获得最佳的Xcode性能。如果可能,并行化构建将确保构建并行运行,并且Find Implicit依赖性将检查项目内的所有依赖项,通常在“Link Binary with Library”构建阶段。 Xcode 10还引入了“并行化目标构建过程”的特性,这意味着依赖目标可以尽快开始编译而不必等待第一个完成,但它必须等到第一个目标的“运行脚本”阶段! 7 (1).png

3.使用新的构建系统

Apple在Xcode 9中推出了新的构建系统,但它并未默认启用。但在使用Xcode 10时,默认情况下,新的版本设置已被启用,并从Xcode文件->项目/工作区设置修改
8 (1).png

如果我们使用xcodebuild从命令行构建iOS项目,那么我们必须传递额外的参数-UseNewBuildSystem = YES这样也会使用新的构建系统。新的构建系统称为xcbuild。

Apple的xcbuild的二进制文件位于路径下:

/Applications/Xcode.app/Contents/SharedFrameworks/XCBuild.framework/Versions/A/Support/xcbuild

4.改进运行脚本阶段

在Xcode中,构建阶段中,我们可以添加自定义运行脚本,以根据我们自己的项目需求构建过程。 9 (1).png

我们已经指定了输入文件到运行脚本阶段,这对于构建系统来决定是否需要运行脚本来执行依赖目标构建是非常重要的。在适用的地方将输入文件提供给运行脚本始终是个好主意。当Xcode 10中的输入文件的数量增长时,给我们指定.xcfilelist格式的所有输入文件的方式,并且我们可以在构建阶段将此文件添加为文件列表输入。 当没有输入文件,更改输入文件或缺少输出文件时,Xcode构建系统将始终运行此构建阶段。添加这些文件非常重要,以避免在不需要时为所有增量构建运行此阶段。在运行脚本阶段的Xcode帮助中也有文档

我们可以在帮助文档中查阅关于“运行脚本”阶段的更多信息。

5.解决循环依赖

Xcode 10新的构建系统为我们分析了循环依赖,一旦项目里面有循环依赖的问题,我们需要处理并解决掉依赖的情况
10 (1).png

Xcode 10里面的帮助系统也为我们解决处理循环依赖的情况提供了几点思路
11 (1).png

12 (1).png

通过错误提示与选项设置,我们可以快速地找到依赖中的问题并处理解决。

6.分析构建时间

使用Xcode 10,我们可以使用计时摘要来分析构建过程,从而帮助我们确定构建所花费的时间,并采取改进措施来发现问题缩短构建时间。 这个新功能将显示构建系统执行的每个任务的时间安排,并且可以在Xcode产品 ->执行操作 - >与时序汇总构建选项中找到。 13 (1).png

在这个条件下,我们构建所产生的输出如下面所示: 14.png 15.png

此功能也可以使用xcodebuild工具使用命令行启用:

$ xcodebuild -showBuildTimingSummary 16.png

这将在日志中打印构建摘要时间。 这样我们可以看到每一步构建花费的时间。 通过使用这种技术,我们可以分析出为了使构建更快而需要完成的工作。

7.编译模式与优化级别

在新的Xcode里面,Apple分离了编译设置“编译模式”和“优化级别”,以便我们可以控制调试版本的编译机制。 编译模式是我们的项目如何构建的模式,我们可以设置我们是否需要优化速度或在调试模式下构建。在调试构建的情况下,整个优化并不总是需要的,这样我们可以将此选项设置为调试的增量构建。 在Xcode 10中,默认编译模式设置为“增量构建”。 17.png

当整个模块停止执行增量构建并在构建过程中构建所有文件时,这将大大加快构建速度。对于调试版本,“优化级别”版本设置可以设置为“不优化”,对于版本版本,可以设置为“优化速度”。我们可以看到在Xcode 10中引入了一个新的选项,即“优化大小”,可用于检查代码大小。这是通过编译应用程序生成的机器代码,而不是应用程序或资产的大小。 18.png

我们可以看到Xcode 10默认启用了正确的选项。如果不使用这些选项,那么应该立即更改这些选项以从新的Xcode功能中受益。

源码级别的改进要点

(1)在Debug Builds设置选项为Incremental

(2)分解处理复杂的表达式

(3)针对复杂的属性,使用使用准确的类型 19.png

(4)在闭包里面提供准确的类型 20.png

这些都会提高编译器的处理速度

8.合理地管理依赖关系

每个应用程序都有内部或外部的依赖关系,依赖可能会减慢构建过程。合理地管理依赖关系是一个很好的做法,在项目中检查依赖关系并正确分类,以帮助Xcode构建系统以更好地执行。依赖关系来自Xcode项目中的各个位置,它们可能是内置的比如头文件,目标依赖项或隐式依赖关系,在链接二进制库,构建阶段依赖项或方案顺序依赖项中。我们必须以更好的速度妥善管理它们

选择正确的依赖管理解决方案。 添加隐式依赖而不是显式依赖 管理项目和工作区,以便轻松访问所有代码 确定将依赖源代码放入存储库或动态下载的策略。 使用依赖管理的缓存策略来加速构建

在构建时,针对依赖关系,我们有下列可以优化的要点 增量式构建都是基于文件的 源文件函数功能的简单改变在构建时不会影响其它文件重新构建 源文件里面函数体外的改变,会影响导致相关文件的重新编译

另外编译器的处理过程都是比较保守的 函数体中的改变不影响文件整体的接口 module内的依赖是基于文件的
targets间的依赖是基于整个target的

9.编译优化

在编译优化加速方面,苹果提到了开发者最好需要限制源文件的接口,具体有下面几点:

(1)保持生成的头文件最小化

(2)在Swift中,使用private尝试减少接口对外暴露

21.png

(3)使用基于block的API

(4)使用category减少接口,这样减少了依赖关系,提高编译速度

(5)保持桥接的头文件最小化

22.png

总结

通过session 408,我们了解到Xcode 10为我们针对构建提供很多辅助功能;这些功能可以帮我们发现问题并解决问题,提高改进构建效率。 我们需要在后续开发中尽力使用相关功能,提高我们的开发效率。

本文总阅读量