iOS包大小优化备忘录
下面是自己在日常App优化过程中,使用过的方案,这里做一个记录。
1.Apple官方优化
App Thinning 是一个关于节省 iOS 设备存储空间的功能,它可以让 App Store 和操作系统在安装、更新及运行 iOS 或者 watchOS 的 App 等场景时,通过一系列的优化,尽可能减少安装包的大小,仅下载所需的资源,减少 App 的占用空间。这个过程包括了三个方案:
- Slicing
- Bitcode
- On-Demand Resources
-
Slicing,会在你向 iTunes Connect 上传App后,对 App 做切割,创建不同的变体,这样就可以适用到不同的设备,这个可以在App的 iTunes Connect 主页中,查看不同设备下载后包大小的差异。
-
Bitcode 是针对特定设备进行包大小优化,但是鉴于App引入一些第三方,往往可能有些SDK不支持开启Bitcode,所以这个功能我使用的很少。但是能开启的情况下,我还是会开启的,毕竟开启后,编译器LLVM会帮助我们生成中间代码,App Store Connect就可以根据Bitcode优化不同芯片的App包。
-
On-Demand Resources 按需加载资源,简单的说,就是在下载App的时候,App中包含的不重要资源不下载,等到需要时,在由系统向苹果的Server发送请求,下载这些资源包。这个在游戏App中使用的比较多,在日常的App中,我还没有使用过,有场景肯定会尝试一下,苹果官网有详细的教程。
2.无用的资源
随着App的迭代,特别是团队开发,App内往往会有一些无用资源,包含图片、字体、音频等。我在项目中,会根据项目内的架构、开发语言,写对应的Python脚本,进行遍历查找,再甄别删除对应的资源。
这里Python脚本就不放出来了,不同的App,肯定写法不一样,比如在我写过的某个App中,图片都是使用宏进行初始化加载的:UIImage *img = RM(@"home_top_icon");
不过网上也有很多非常好用的工具,比如:LSUnusedResources
3.资源压缩
这里的资源压缩,我分为了2个:
- 字体压缩
- 图片压缩
3.1 字体压缩,是很多人忽略的地方,如果UI小姐姐设计的页面中,我们只是使用了某个字体的英文、或者仅仅使用了数字,那么是可以进行字体压缩的。
字体文件中包含的绝大多数字符都是不会被用到的。若有办法将用到的字从字体文件中单独拿出来组成一个子集字体文件,则能减轻不必要的网络加载资源浪费。这里就要提到一个工具:Fontmin。
其实也可以问UI小姐姐,让UI小姐姐帮忙删除里面用不到的字符,一般的字体编辑器都能做到。比如 Fonteditor
相信我,删除用不到的字符之后,你会惊讶字体文件大小的变化的😄😎。
3.2 图片压缩,基本都会使用的办法,在开发中,我会要求大于10K的图标,在拉到项目之前,都要经过压缩,而使用的压缩工具,一般是网页工具 TinyPng 或者GUI工具 ImageOptim 进行图片压缩。
需要注意的是,很多团队,会使用 WebP 格式的图标,使用 WebP 的确大大减少了体积,但是却要引入 WebP 解码框架,不管你使用的SDWebImage
还是Kingfisher
;而且Xcode当前还不支持预览 WebP图片。所以是否使用 WebP 格式 看团队和App的性质。
4.云资源
这个很好理解了,我搭建了DevAps平台,我称之为App的运维平台,在其中的云资源模块,开发者可以很容易配置云资源,比如json、png、zip等文件,自动生成对应的链接。
这样子,我们可以将App内很多低频率模块的图片资源、配置文件等,放到云平台上,用到的时候,动态加载即可。注意图片的frame要设置好。
5.代码清理
一般的无用代码筛查方式可以分为动态和静态两种方式。静态的方式主要是通过代码扫描、参与编译构建过程或者分析最终产物来确认哪些代码没有被用到。而动态的方式主要是靠插桩或者运行时信息来获取哪些代码没有执行。由于 Objc 强大的动态特性,我们在样本量足够大的场景使用动态方式会比静态方式准确率高很多。
静态筛查筛查方案:
比较简单的方式是:基于 otool dump
最终产物中的 __objc_class_list & __objc_class_refs
做差集找到未使用的 Objc
类。
如果代码采用 C 、C++ 等静态语言编写代码时,编译期已经确定了基本的代码逻辑,所以编译器会帮助我们将没有使用到的代码标记为 Dead code
最终不会打包到安装包中。
动态筛查方案:
- 基于插桩的行级别代码覆盖率
- 基于
Runtime
的轻量级运行时「类覆盖率」方案
这部分的主要思路,来自下面的团队:
说实话,操作上面这些,需要有很强的计算机基础,比如汇编。加上目前我写的项目都是纯Swift的,所以我暂时没有尝试过这种办法。
但是,在日常开发中,我们有时候会使用一些轮子,但是很多时候,这些轮子功能是非常多,而我们仅仅使用其中很少一部分功能,所以我一般会在优化时间,直接自己实现对应的功能,或者对轮子、SDK进行剪裁,这样也能减少项目中一些冗余的代码。
6.编译器设置
在iOS工程中,Xcode为我们提供了编译器优化的一些选项,比如对于 Objective-C 语言,我们可以在 Xcode -> Build Setting -> Apple LLVM 9.0 - Code Generation -> Optimization Level 中进行设置:Fastest Smallest[-Os]
其他选项的一些含义:
-
None[-O0]
: 编译器不会优化代码,意味着更快的编译速度和更多的调试信息,默认在 Debug 模式下开启。 -
Fast[-O,O1]
: 编译器会优化代码性能并且最小限度影响编译时间,此选项在编译时会占用更多的内存。 -
Faster[-O2]
:编译器会开启不依赖空间/时间折衷所有优化选项。在此,编译器不会展开循环或者函数内联。此选项会增加编译时间并且提高代码执行效率。 -
Fastest[-O3]
:编译器会开启所有的优化选项来提升代码执行效率。此模式编译器会执行函数内联使得生成的可执行文件会变得更大。一般不推荐使用此模式。 -
Fastest Smallest[-Os]
:编译器会开启除了会明显增加包大小以外的所有优化选项。默认在 Release 模式下开启。 -
Fastest, Aggressive Optimization[-Ofast]
:启动 -O3 中的所有优化,可能会开启一些违反语言标准的一些优化选项。一般不推荐使用此模式。
Swift 语言的编译器是 swiftlang,同时也是基于 LLVM 后端的。Xcode 9.3 版本之后 Swift 编译器会提供新的选项来帮助减少 Swift 可执行文件的大小:
Swift.png参考资料:
7.其他小技巧
7.1 使用SF Symbols图标,SF Symbols 在 WWDC 2019 期间推出符号图标,支持彩色模式,目前SFS已经有几千个图标了,具体的使用可以参考我的这篇文章。
7.2 多个不同色彩的图标,只加入到项目里一种颜色即可。使用的时候,对于不同的颜色,我们可以异步渲染成对应的颜色,并且缓存,由于图标一般都很小,这样做的收益还可以。举个例子,UITabbar
一般选中和未选中2个图标,那么我就可以只使用选中的图标,未选中的自己渲染即可。
END。
我是小侯爷。
在帝都艰苦奋斗,白天是上班族,晚上是知识服务工作者。
如果读完觉得有收获的话,记得关注和点赞哦。
非要打赏的话,我也是不会拒绝的。