APP 的大小是分为 APP 下载大小和安装大小两个概念的。
- 下载大小是指 App 压缩包(也就是 .ipa 文件)所占的空间,用户在下载 App 时,下载的是压缩包,这样做可以节省流量;
- 当压缩包下载完成后,就会自动解压,解压过程也就是通常所说的安装过程;安装大小就是指压缩包解压后所占用的空间。
用户在App Store看到的大小是安装大小。如果想看安装包在各机型上的下载、安装大小可以在 App Store Connect 后台查看。
App Store OTA 下载大小限制:
虽然苹果历年都会调整 App 下载大小,由之前的 100M 到后来的 150M 再到现在的 200M。如今,App 下载大小超出 200 MB 时 ,会出现两种情况:
- iOS 13 以下的用户,无法通过蜂窝数据下载 App;
- iOS 13 及以上的用户,需要手动设置才可以使用蜂窝网络下载 App。
图片减包
相比起代码(5kb/千行)的平均占用来说,对图片进行减包是API包瘦身的最直接、最高效的手段
对图片资源的处理方式包括四种
- 通过请求下载大图
- 使用工具压缩图片
- 删除重复图片
- 查找复用相似图片
方式1需要推动落地,所以本文不讨论这种处理方式。
图片压缩
为什么png能够无损压缩?
UI 同事提供的PNG图片,一般都是photoshop导出的,图片存在大量的额外信息。
png图片有两种类型的数据块,
- 一种是必不可缺的数据块称为关键数据块。
- 另一种叫做辅助数据块,辅助数据块在png文件中占据了极大的篇幅,正是这些数据块构成了png的无损压缩条件
工具推荐:ImageOptim 、图压 、tinypng
tinypng进行无损压缩,压缩率一般在60%-70%之间,非常高效,缺点是只能线上压缩
图片资源压缩也有很多公司使用webp
格式图片,这种格式对比png
、jpg
要小很多,但客户端性能对解码webp
图片会有一定的影响。在一些老的机型上,表现不是很好。所以需要做一些取舍。这里有webp
和png
格式转换的工具 isparta
其实我觉得这两种方案可以共存,团队内部设定一个资源文件的最大值,比如超过 100KB
就选择使用 webp
格式的图片,小于 100KB
就使用压缩工具。这样就可以极大的减少资源文件的大小,同时减少包的体积。
删除重复图片
通常来说,出现重复图片的原因包括 模块间需求开发没有打通 或是 缺少统一的图片命名规范。通过图片MD5摘要是识别重复图片的最快方法
也可以使用工具 LSUnusedResources 进行处理
代码文件优化
代码文件优化其实可以看成是对可执行文件 Mach-O 的优化,其大小是由代码量决定的。所以对 Mach-O 瘦身,其实就是查找并减少无用的代码
无用代码检测
减少第三方SDK
- 检查是否导入了相同的功能的库
- 第三方库大小占比很高时,考虑是否要替换掉它
编译优化
修改 Build Setting 的相关配置,可以让我们在更快的编译速度、更小的二进制大小、更快的执行速度之间自由选择。这种方式的性价比很高,改动一项配置,就可能会带来收益,但是可能具有一定的风险,需要谨慎。
-
Build Settings -> Architectures -> Excluded Architectures
删除无用架构
- 模拟器 32 位处理器测试需要 i386 架构;
- 模拟器 64 位处理器测试需要 x86_64 架构;
- 真机 32 位处理器需要 armv7, 或者 armv7s 架构;
- 真机 64 位处理器需要 arm64 架构。
结论:
该编译项在Release
下的配置应如下
Any iOS SDK 设置为armv7 / &armv7s
;Any iOS Simulator SDK 设置为arm64
意思是Release模式下真机排除armv7 和 armv7s指令集,模拟器排除arm64(模拟器不支持arm架构)
-
Build Setting - Link-Time Optimization
链接时优化 LTO(Link-Time Optimization)
对整个程序代码进行的一种优化,是 LLVM 里在链接时进行跨模块间的优化。
LTO 优化的优点:
一、将一些函数內联化:不用进行调用函数前的压栈、调用函数后的出栈操作,提高运行效率与栈空间利用率;
二、去除一些无用代码:如果一段代码分布在多个文件中,但是从来没有被使用,普通的-O3
优化方法不能发现跨中间代码文件的多余代码,因此是一个局部优化。但是Link-Time Optimization
技术可以在link
时发现跨中间代码文件的多余代码;
三、对程序有全局优化作用:这是一个相对广泛的概念。举个例子来说,如果一个if
方法的某个分支永不可能执行,那么在最后生成的二进制文件中就不应该有这个分支的代码。
LTO 优化的缺点:
一、LTO 会降低编译链接的速度,所以建议在打正式包时开启;
二、开启了 LTO 之后,Link Map 的可读性明显降低,多出了很多数字开头的类(LTO 的全局优化导致的),所以如果需要阅读 Link Map,可以先关闭 LTO;
有三种选项可供选择:
No
: 默认项,不开启链接期优化
Monolithic
: 生成单个LTO
文件,每次链接重新生成,无缓存高内存消耗,参数 LLVM_LTO=YES
Incremental
: 生成多个LTO
文件,增量生成,低内存消耗,参数 LLVM_LTO=YES_THIN
结论:
该编译项 Release
模式下应将 Link-Time Optimization
设为 Incremental
-
Code Generation -> Optimization Level
语言编译优化
-
OC
优化:Build Settings -> Apple Clang - Code Generation -> Optimization Level
-
Swift
优化:Build Settings -> Swift Compiler - Code Generation -> Optimization Level
OC
内联优化参数如下:
- None[-O0]:Debug 模式下默认开启,编译器不会优化代码,意味着更快的编译速度和更多的调试信息
- Fast[-O, O1]:编译器会优化代码性能并且最小限度影响编译时间,此选项在编译时会占用更多的内存
- Faster[-O2]:编译器会开启不依赖空间 / 时间折中所有优化选项。在此,编译器不会展开循环或函数内联。此选项会增加编译时间并且提高代码执行效率
- Fastest[-O3]:编译器会开启所有的优化选项来提升代码执行效率。此模式编译器会执行函数内联使得生成的可执行文件会变得更大。不推荐使用此模式
- Fastest Smallest[-Os]:编译器会开启除了会明显增加包大小以外的所有优化选项。默认在
Release
模式下开启- Fastest, Aggressive Optimization[-Ofast]:启动 -O3 中的所有优化,可能会开启一些违反语言标准的一些优化选项。不推荐使用此模式
结论
使用默认配置即可,无需修改
Swift
编译内联优化参数如下:
- No optimization[-Onone]:Debug 模式下默认开启;不进行优化,能保证较快的编译速度
- Optimize for Speed[-O]:Release 模式下默认开启;编译器将会对代码的执行效率进行优化,一定程度上会增加包大小
- Optimize for Size[-Osize]:编译器会尽可能减少包的大小并且最小限度影响代码的执行效率。
核心原理
:是对重复的连续机器指令外联成函数进行复用,和函数内联的原理正好相反。因此,将其开启,能减小二进制的大小,但同时理论上会带来执行效率的额外消耗,对性能(CPU)敏感的代码使用需要评估
配合其使用的还有Compilation Mode
设置,其含有两个选项
1、Single File:
单个文件优化,可以减少增量编译的时间,并且可以充分利用多核 CPU,并行优化多个文件,提高编译速度。但是对于交叉引用无能为力
2、Whole Module:
模块优化,最大限度优化整个模块,能处理交叉引用。缺点不能利用多核 CPU 的优势,每次编译都会重新编译整个 Module
Relese 模式下 -Osize 和 Whole Module 同时开启效果会发挥的最好
结论:
Release
模式下配置为Optimize for Size[-Osize]
,Compilation Mode
选项改为Whole Module
-
Swift Compiler - Code Generation -> Compilation Mode
编译模式优化
结论:Release
模式下配置为Whole Module
-
Linking -> Dead Code Stripping
清除无用代码
构建完成后,如果是 C、C++ 等静态的语言的代码、一些常量定义,如果发现没有被使用到将会被标记为 Dead code
。开启 DEAD_CODE_STRIP = YES
这些 Dead code
将不会被打包到安装包中
结论:默认配置即为 YES,所以使用默认配置即可,无需修改。
-
Deployment -> Strip Style
去除符号
表示的是我们需要去除的符号类型的选项,其分为三个选择项:
- All Symbols: 去除所有符号,一般是在主工程中开启;
- Non-Global Symbols: 去除一些非全局的 Symbol(保留全局符号,Debug Symbols 同样会被去除),链接时会被重定向的那些符号不会被去除,此选项是静态库 / 动态库的建议选项;
- Debug Symbols: 去除调试符号,去除之后将无法断点调试。
结论:主工程选择All Symbols
,静、动态库选择Non-Global Symbols
其他
- LinkMap 可以得出每个类或者库所占用的空间大小(代码段+数据段),方便开发者快速定位需要优化的类或静态库 (用这个我找到了一个非常大的背景图,2x的2M,3x的将近4M)删除后效果非常明显
参考:
图片减包
iOS开发之ipa瘦身初探
iOS 优化 - 瘦身
搜狐技术:iOS包体积优化实践
推荐一个好用的工具:
WBBlades重要节点更新-专为提效而设计
基于Mach-O文件解析的工具集,包括无用代码检测(支持OC和Swift)、包大小分析(支持单个静态库/动态库的包大小分析)、点对点崩溃解析(基于系统日志,支持有符号状态和无符号状态)。主要利用了__Text汇编代码分析、架构提取、符号表剥离、DYSM文件提取、崩溃文件(ips)解析等技术手段实现。
图片相似度对比原理:像素差异比对;神经网络算法;感知哈希算法
https://www.beyondcompare.cc/jiqiao/bc-iaitpdnb.html
https://blog.csdn.net/SummerCloudXT/article/details/82629348
WWDC22 App 包大小优化
https://xiaozhuanlan.com/topic/4675012938