学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

【译】Improve Performance in Your iOS Applications 一

翻译几篇iOS性能优化的文章 Ⅰ,在第一篇文章中,我们将专注于iOS性能提示,帮助您提高iOS应用程序的编译时间,更快地构建,并在构建系统中改进iOS性能。让我们开始吧。

前言

自2007年成立以来,随着时间的推移,苹果和iOS生态系统进行了大量的变化和新功能的添加(或删除),得到了极大的改善。与此同时,应用程序和数据的规模一直在增长。这对你手中的强大设备——iOS设备有自己的影响。

开发人员努力设计最好的体验,常常在速度和性能上做出妥协。随着时间的推移,这最终会导致应用程序性能下降,导致糟糕的用户体验,甚至更糟糕的是——用户删除你的iOS应用程序。为了解决这个问题,我们将介绍一个由4部分组成的性能系列,重点介绍如何改进iOS应用程序。

只编译必要的部分

让我们从一行调整开始,它可以将大部分构建时间减半。

您知道您所有的文件都要构建多次吗?Xcode中的一个重要观察结果可以回答这个问题。这篇相对较老的Stack Overflow文章仍然适用于最新版本的Xcode。

解决方案是:在你的Xcode中,对于所有非必要或非发布的构建类型,将build Active Architectures Only设置为Yes。因为构建设置通常不只是发布和调试设置(不像大多数项目),您可能会看到默认情况下为开发使用的所有配置启用了此标志。

要更新设置,您需要在项目导航器中导航到项目文件,然后在左边栏中选择您的项目并导航到Build Settings选项卡。在Architectures下,您将发现Build Active Architectures Only的设置,您需要将其更改为YES

在大多数情况下,这应该理想地将构建时间减少到50%,但可能根据应用程序架构和其他配置而有所不同。神奇,不是吗?让我们继续下一个。

加快Swift的编译时间

你知道吗,默认情况下,整个模块优化(Whole Module Optimization)也会影响你的构建生成和构建速度?默认情况下,这只在release时启用。

下面是如何调整它:

对于debug模式,在设置Swift Compiler-Code Generation下开启enable整个模块优化模块(Whole Module Optimization),然后在Other Swift Flags,下,为debug模式指定以下标志:

-Onone

此外,在debug构建设置下,将Optimization Level (优化级别)的值设置为FastOptimization Level (整个模块优化)。

如果你使用CocoaPods,在Podile底部添加以下代码将有助于优化所有依赖项:

1
2
3
4
5
6
7
8
9
10
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
if config.name == 'Debug'
config.build_settings['OTHER_SWIFT_FLAGS'] = ['$(inherited)', '-Onone']
config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] = '-Owholemodule'
end
end
end
end

这将确保一次编译整个模块,而无需为debug版本优化代码,以加快开发和节省时间

实测

左边未开启优化Podfile 右边开启

分析你的iOS编译时间

许多开发人员面临的问题是,他们的代码的编译时间相对较长。更令人担忧的事实是,有时它会突然变得比以前的构建时间更慢。这可能会迅速升级。这种技术可以帮助您定位编译器难以处理的代码部分,并且您可以尝试重新排序它们以加快编译速度。

第一步是导航到你的Swift Compiler部分,在Other Swift flags下添加以下标志:

-Xfrontend -debug-time-function-bodies

使用上述标志,编译器将打印出每个函数所需的时间。下一步是收集所有这些日志数据点并对它们进行排序。输出将给出文件名和行号。在大多数情况下,只有少数几个函数(有时甚至少于5个)被重复打印—所以不用担心。

请记住,您不需要对编译器有太多了解就可以理解或理解日志输出。只要观察每行末尾的函数名和关键字就会明白了。

重写几行代码可能会使整个项目的构建速度加快50%。这是因为在大多数情况下,错误线展示了特定于数据结构的改进,以及如何编写更合适的代码。

只需将函数重写为尽可能基本的函数就可以解决问题——表面上不必要的类型定义、中间变量、可变变量等等。

这是一个简单但极好的例子

实测

调试日志在Xcode的报告导航器中可见,但只能手动展开每个单独的文件:

升级系统配置

Xcode 9.2之上,新引入的架构允许项目并行运行Swift编译任务。如果您的计算机没有足够的RAM,那么构建时间可能会大大减慢。你可以做几件事来避免这种情况:

  • 使用以下命令禁用Xcode的并行任务执行:

    defaults write com.apple.dt.Xcode BuildSystemScheduleInherentlyParallelCommandsExclusively -bool NO

  • 有些项目会比其他项目运行得更快更好。另一件需要注意的事情是:如果您的机器没有很多RAM,这实际上可能会降低构建的速度。如果应用上述方法使构建变慢,最好使用以下命令禁用并发构建任务:

    defaults delete com.apple.dt.Xcode BuildSystemScheduleInherentlyParallelCommandsExclusively

  • 如果你在你的机器上有一个超快的配置,可以加快你的编译时间,你可能会考虑通过使用下面的命令分配一些额外的进程Xcode:

    defaults write com.apple.Xcode PBXNumberOfParallelBuildSubtasks 4

  • 在您的工作空间设置(workspace settings )或项目设置(project settings)中,在Shared Workspace Settings(共享工作空间设置)下启用new build system(新的生成系统),以加快生成速度。一旦被选中,new build system(新的构建系统)将控制编译和构建生成,减少编译Swift代码所需的时间。

  • 增加内存RAM或升级系统硬件(M1速度很快)。

改进iOS编译过程

如果为您的项目启用了并行编译,您将看到更好的编译性能,编译目标的顺序也不会那么重要。然而,如果使用上述技术关闭了并行编译生成,Xcode将尝试按照构建操作方案(the build action scheme)中指定的顺序构建目标依赖项(target dependencies)。

在这种情况下,您可以声明输入和输出配置,以让构建系统知道哪些脚本任务可以避免不必要的重新运行。

您可以在工作空间中采用的另一种技术是避免自动链接项目依赖项( avoid auto linking project dependencies)。这个特定的设置允许编译器自动链接与您导入的模块相关的框架,而无需在链接库的构建阶段显式地链接它们。然而,自动链接本身并不在构建系统级别建立对指定框架的依赖。这意味着,在您尝试链接依赖项之前,构建系统或自动链接特性不能保证实际构建了项目所依赖的目标。

相反,你应该添加显式依赖以让构建系统知道,你也可能更喜欢通过拖放技术将项目与其他Xcode项目引用到当前项目的文件导航器中,并捕获其他所需项目的目标。

优化iOS崩溃报告生成

iOS生成一个dSYM文件来维持原始代码和模糊堆栈跟踪之间的映射,以理解崩溃报告。由于生成dSYM文件需要大量的时间,所以在the debugger is not attached(没有附加调试器)的情况下,或者在调试构建生成期间,可以避免生成dSYM

Debug Information Format (调试信息格式设置)下,确保您使用的是普通的DWARF而不是带有dSYM文件的DWARF

Objective-C头文件是罪魁祸首

对于遗留项目,您可能已经注意到,许多项目文件都以详尽的头声明开始。您知道在项目中声明额外或不必要的头文件导入会大大降低生成构建的时间吗?

此外,大量的头文件还成倍增加了依赖项检查所需的时间。

在这种情况下,以下策略就派上用场了:

  • 减少头文件的导入,只导入必要的头文件;你可以很容易地使用代码编译和检查工具,如SwiftLint

  • 使用前向声明而不是递归头文件包含或头文件调用另一个头文件,以大幅减少依赖的数量,并最终提高构建生成时间(这个链接应该很有帮助)。

减少使用非透明视图

不透明视图(An opaque view)是不透明(transparency)的视图。这意味着处于不透明视图后面的任何元素在层后面都不可见。

An opaque view is the view that does not have transparency. This means that any elements placed behind the opaque view are hidden behind the layer.

在关注iOS性能提升的同时,这也是每个人都需要理解的一个关键领域。将视图设置为opaque可以确保在框架绘制视图之前应用系统优化,肯定会有一些改进,特别是在呈现屏幕时绘制的性能。

如果任何视图的透明度或alpha通道低于1.0,iOS框架必须在处理视图层次结构时将不同的视图和层混合在一起,从而计算最终的显示渲染。相反,如果任何视图有一个不透明的属性设置,iOS框架就会把这个视图层叠加在之前的视图层之上,并避免任何额外的处理来混合视图层次结构。

1
label.backgroundColor = UIColor.clear // Original label.backgroundColor = UIColor.white // To be applied

此外,要从图像中删除alpha通道,你可以使用预览应用程序(Preview app)。所有你需要做的是通过按Shift + CMD + S和取消Alpha复选框来复制图像,同时保存重复的图像。

调整你的Swift扩展

当你在Swift中编写自己的扩展时,默认情况下,它们被设置为public access modifiers(公共访问修饰符)。这增加了编译时间,因为每个Swift类都要使用它。

解决这个问题的最好方法是完全避免创建扩展。如果没有,那么您应该创建扩展并将访问修饰符设置为private,因为您只会在代码的一部分中使用它们。

删除不必要和不使用的代码

无论是pods还是在你的iOS应用程序中不再起任何作用的代码,删除不使用的代码、不使用的图像文件或assets(资产),许多不再使用的pods甚至是函数,都将大大减少构建时间,同时减少发布版本的大小。

让你的iOS应用模块化

对于相对大型的项目,根据需求,提高iOS应用性能最有效的解决方案之一是将庞大的iOS项目转化为模块化结构。对于这种包含多个特性和功能的大型项目,您可以考虑采用模块化体系结构,其中每个模块由统一的特性组成,这些特性在同一模块中彼此紧密相关。

其想法是用workspace(工作空间)替换project(项目),并构建可以编译和注入到工作空间中以供利用的模块。一般来说,坚持关注点分离,你可以将你的iOS应用模块化在几个模块中,如数据库、网络、utilities (实用程序)、接口等。

模块化你的iOS应用程序的好处不仅是减少编译时间,而且你可以很容易地在整个项目和其他项目中重用模块,只需要导入它们,而不是每次都重写整个代码。

利用缓存

通过“一次构建,处处重用”的方法——远程构建缓存策略有助于大幅减少构建时间,因为您不再需要重新构建任何机器上已经构建的任何东西。

在这种方法中,只要所有输入文件和编译设置相同,就可以下载从另一台计算机生成和共享的工件,而不是在本地生成目标。找到合适的缓存级别对于远程缓存的成功至关重要。过于细粒度的缓存单元(缓存编译过程的每个阶段)可能会导致严重的网络流量开销,超过所节省的CPU。另一方面,将整个软件放入一个可缓存单元,可能会显著降低缓存命中率;每一次本地更新都会使远程访问的缓存构件失效,强制在本地完成构建。

另一种充分利用缓存的方法是使用Bazel build system。对于iOS系统,Bazel提供了一套预定义的苹果规则,帮助你开始并构建一个完整的应用程序。然而,请注意,设置Bazel并使用Bazel成功运行你的iOS版本需要高级水平的理解,但在我看来这是值得的,特别是对于大型项目。包括LyftPinterestLinkedIn在内的一些重要机构也利用Bazel开发他们的iOS应用程序。

多亏了Bazel,构建只执行一次,而重构只在任何目标文件更改时执行。一旦您使用-remote_http_cache将其指向远程缓存,我们就可以在共享远程缓存中共享此构件。你可以在这里这里了解更多关于为iOS应用程序设置Bazel的信息。

调整iOS应用程序测试设置

Apple iOS测试模拟器可以用于在各种软件和硬件组合上进行应用程序测试。通过为模拟器选择“Physical Size”或“Pixel Accurate window size”,可以减少运行的测试数量和执行测试所需的时间。这些配置更改使用更少的资源,并防止通过模拟永远不会看到的像素而降低测试速度。

这里是Stack Overflow的答案,关于如何配置您的iOS模拟器的物理大小或像素精确窗口大小,以利用iOS性能测试工具。

用Sentry监控你的iOS性能

Sentry的iOS SDK自动报告错误(s)或异常(s)未被捕获在您的应用程序中导致应用程序崩溃。在iOS上集成Sentry所需的最低版本是9.0。

虽然使用Cocoapods安装SDK的推荐方法如下所示,但iOS应用也支持其他安装方法:

1
2
3
4
5
6
7
8
platform :ios, '9.0'
use_frameworks! # This is important

target 'YourApp' do
pod 'Sentry', :git => 'https://github.com/getsentry/sentry-cocoa.git', :tag => '7.11.0'
end

#run pod install

通过性能监视,Sentry可以跟踪您的软件性能,测量吞吐量和延迟等指标,并跨多个系统显示错误的影响。

在Sentry的帮助下,iOS性能监控可以通过两种方式实现:

  • 自动检测允许您捕获Sentry已经指定和支持的标准跟踪,例如捕获应用程序加载时的事务,或当应用程序启动时(包括冷启动和热启动),检测慢速和冻结帧、网络调用、文件和I/O操作等。您可以按照这里给出的步骤启用自动检测。

  • 自定义插装允许您指定自己的实现并创建事务以捕获可能的每一个操作。这可能因情况的不同、特性的不同而有所不同,例如当用户在购物车上执行结帐、验证支付细节或首次注册时,等等。您可以按照这里给出的步骤启用自定义插装。

Sentry支持大量的集成,通过不同的工作流程和提供程序来实现您的合适目标。

结论

在本文中,我们介绍了如何提高iOS应用程序性能、iOS性能测试工具、iOS性能监控等。我们主要关注构建过程和构建系统。虽然我们的努力是将所有内容集中在一个屋檐下,但iOS性能技巧的话题是广泛的,应该给予适当的公正。

参考

Improve Performance in Your iOS Applications
有道翻译
Profiling your Swift compilation times