优化包大小的常用方法和如何查看优化效果

1,411 阅读9分钟

前言

最近项目不那么忙了,开始弄一些优化,打算先从包体积、启动时间、编译时间入手,项目最大的时候已经达到了140m+,因为本身项目的功能并没有那么复杂,所以这么大的包体积明显有点不太合理。so,弄它。

既然要优化包体积首先要明白一件事,优化的是什么?或者说 你优化的目标是什么,面向用户还是面向开发者?对于我来说,我肯定是面向用户的,看的是最后实际的包大小,也就是appStore中显示的大小。那什么是面向开发者呢?那就是看利用xcode打出来的包的大小,可能包含多cpu架构,多种图片,这种是不准确的。有的兄弟可能会问了,那到底应该怎么看优化的效果呢?我自己也看了很多关于优化包大小的文章,都只说了优化的方法,最主要的是还有很多没用的方法,更没提到如何看优化效果。

如何查看优化效果

在我一顿操作之下,我找到了两篇比较有用的文章1文章2,解释了什么是下载大小、什么是安装大小,为什么以某个机型导出的ipa比线上的小。可以以7p(随便)为准,只导出7p的包,然后对比优化前后ipa中的各个文件的大小。虽然这个也不是非常准的,但是相比直接查看build之后的Pruducts的.app中的ipa包,还是很准的。还有一个终极大招,直接上传到appStore,然后查看构建包的安装大小,那是非常非常准滴。最后跟大家说一下我平常是怎么操作的: 以某个机型导出ipa包,包含四个ipa包,每个包大小都不同,我也不知道为啥会有四个,可以以最大的那个包作为基准,前后的ipa包对比主要文件的大小,包括:Frameworks(主要是三方库)、可执行文件(exec),Assets.car(.xcassets中的图片),一些其他的资源文件:视频、音频、json、其他图片等。 每次优化过后都要记录一下优化方法和优化效果。 但是最终效果还是以构建包的安装大小为准

优化包体积 无外乎两种途径:资源文件和可执行文件,资源文件相对简单一点,保证质量的情况下尽量压缩就可以了。 最主要的最考验技术的是如何减小可执行文件的大小。下面看看大概都有什么方法!

优化方法

1:去除多余的架构有用吗?并没有,减小的只是导出的包大小,跟appStore没有一点关系

2:去除无用的代码,无用类、结构体、枚举、函数、变量等,值得一提的是有些三方库很大,但是你只需有其中的一个小功能,如果时间充足的话建议你拆出来或者自己写。有用

3:三方以静态库的形式集成,有用,这个减少的效果非常明显(动态库和静态库的启动时间差别大吗?这个以后再说,实践出真知),有的文章说动态库比静态库小,不知道是怎么测出来的……,他可能说的二进制,而不是说的app。

4:去除无用的资源文件(去除无用图片利器:LSUnusedResources)有用,压缩资源文件也有用(视频、音频、json等),图片最好放在xcassets中有用,png格式,2x、3x各一个,1x现在过时了,不要放了,当然,放了也无所谓,毫无影响。

5:压缩图片有用吗?没啥用,利用tiny压缩一次就可以了,放在xcassets中的图片,苹果会帮你优化,过度优化是没用的(苹果会给你还原回来),除非使用特殊的压缩方法,文章最下方有头条的几个不常见的优化方法。

6:开启 Pod 库和主工程 Xcode Build Settings 中的 ASSETCATALOG_COMPILER_OPTIMIZATION space 选项,改变图片压缩算法,有用

7:使用链接时优化 Link-Time Optimization(LTO):设置为YES或者YES_THIN(incremental),最好设置成incremental 有用

8:build setting几个默认选项我就不提了,值得一提的是Generate Debug Symbols(GCC_GENERATE_DEBUGGING_SYMBOLS),意思是启用或禁用调试符号的生成,如果设置成NO,也就不会生成DSYM符号表。首先可以确定的是它和去除多余架构是一样的,没啥用,减小的只是导出的包大小,没有符号表怎么查线上的bug呢?再自己生成一份?有点多此一举了,所以还用xcode默认设置YES就行,不用改。

这里有必要解释一下1和4,为什么多cpu架构和图片的1x、2x、3x不会引起安装包的大小呢?apple有一项技术叫做app thining,看这个thining就知道是用来做应用瘦身的,它有三种优化方式,分别为Slicing、bitCode和On-Demand Resources,感兴趣的可以点击查看官方资料,咱们现在只看Slicing,原文:

Slicing is the process of creating and delivering variants of the app bundle for different target devices and operating system versions
Slicing是为不同的目标设备和操作系统版本创建和交付应用程序包变体的过程。Slicing仅包含目标设备和操作系统版本所需的可执行架构和资源

这回算是彻底明白了,也就是说不管你打出来的包包含多少个cpu架构,图片是否包含1x、2x、3x,从商店下载的时候只会下载对应的手机的架构和图片。所以打包的时候去掉多少个架构跟安装包包大小没有一点关系。

9:之前参加了在快手总部举办的北京场的技术沙龙,其中关于美团技术人员 靛青 分享的主题 “使用 Global Machine Outliner 缩减重复代码” 我做了一下实践,优化效果如图:

企业微信截图_c89194ec-909e-44e5-9ff9-8392be64ab60.png 其实这个优化效果已经出乎我的意料了,我做之前以为也就能优化一两兆,没想到能优化4m多。

原理: outline1.png 如果大家稍微了解过编译原理,肯定会知道文件的编译过程:预编译、词法分析、语法分析、语义分析、生成LLVM中间代码IR、优化器优化、生成汇编转为机器码再生成目标文件.o、链接所有的目标文件生成可执行文件,这是oc的前端编译器clang的步骤,swift用的不是clang,它有自己的前端编译器swiftc,步骤其实大同小异,只不过中间多了一个中间语言SIL。这里也提现了LLVM的强大,我后端处理器不用动,你自己随便弄语言,随便弄前端编译器,只要你最终代码能让LLVM后端处理,那就可以,不多说了,有兴趣的可以自行查阅资料。

其实outline利用的就是IR语言,通过上面的编译步骤可以得到所有的文件代码都需要通过IR这一步骤,不管你是oc还是swift,所以在这一步处理没有任何问题,如果在SIL阶段处理,那oc就歇菜了……。

so,大招来了,如果把所有的IR文件合并在一起,查找到重复的指令序列,那咱们就可以合并掉它。看一下完整流程:

企业微信截图_f52e80cf-b9d7-46ae-92fd-06ab89b9b7f2.png 靛青也给大家做了一个demo,可以下载来尝试一下。使用方法也很简单:

得到的.png

靛青原文
总结:
• Machine Outliner 通过合并重复指令序列减少指令个数
• 通过 --machine-outliner-reruns 参数指定重复 Outline 的次数可以进一步减少指令个数
• Machine Outliner 在 IR 转换为机器码的过程中执行,为了实现全局 Outline 需要合并所有 IR 为一个大 IR
• LTO 完美匹配了全局 Global Machine Outliner 需要的条件

用前需知:
• 目前 Xcode 12.5 已经集成了全部功能
• 需要 outline 的组件需要使用源码编译并开启 LTO 选项
• 构建时间真的很长
• 由于增加了调用,实际应用可能存在性能影响
• 不建议用在小型 App 上

原作者说构建时间很长,但是我感觉还好,跟没用outline也没差几分钟,也可能是我的项目小😄,虽然我的是小型app,但是带来的效果也是很明显的,4m多很可以了。另外我在实践的时候还是碰到了挺多坑的,因为我之前是用动态库的,然后切到静态库就有问题,build setting的关于trip的设置也可能会影响到outline,导致编译失败,还有就是上传构建包的时候符号表有问题(还没解决……)ps:不知道什么问题?那就clean 在build, 不行就重启xcode。

注意:动态库没办法做全局 outline,三方库要用静态库。另外也不要在 debug 下做这个 outline 优化,因为 debug 下 -O0 会在 ir 上插入一个 optnone 标记,这个标记会阻止后续所有的优化操作,会影响调试期间构建时间,所以这个优化只在打包时候做。

还有其他的一些优化方法: 三方的图片资源文件改用asset、Assets.car 合并、使用 RGB with palette 压缩图片、_TEXT 段迁移等等高端一点的、修正 Exported Symbols 配置、属性动态化、二进制段压缩, 这些参考今日头条的优化吧,我还没实践过。

优化效果

项目最大的时候有140m+,受不了了,开始优化,首先从简单的入手,能压缩的资源文件都压缩,把一些表情和动画文件放到后台。代码我倒是没动,虽然有很多暂时不用的代码,但是就怕哪天产品要把老功能放开,在加回来就太麻烦了。所以资源文件优化这一步减少了40m左右,然后又是三方库由动态库改为静态库,LTO=YES_THIN,之后使用了outline和图片压缩算法ASSETCATALOG_COMPILER_OPTIMIZATION设置为space还有其他的一点小细节减少了27m左右。总共减少了将近70m,已经很满意了😄,之后再研究研究别的方法,应该降到70m以下不是问题。

结束

以上就是全部内容了,能力有限,有好多其他的方法并没有说到,之后实践过后再加上,大家如果对哪个方法感兴趣,我可以给大家详细讲讲,拿出具体的优化数据。如有错误,欢迎指正。

不知道有没有小伙伴不知道利用pod怎么把动态库转为静态库、把所有的三方中的LTO设置为TRUE_THIN的,或者下载outline的demo实践在自己项目中无法成功的?如果有,就说出来,我再写一篇文章讲讲具体实践方法。