目录
前言
一、安装包组成
二、资源瘦身优化
2.1 无用资源删除
2.2 资源压缩
2.3 图片管理方式
2.4 动态下载资源
三、可执行文件优化
3.1 找到方法和类的全集:Link Map 文件分析
3.2 找到已使用的方法和类: Mach-O 文件
3.3 使用AppCode
四、编译选项优化
总结
随着版本迭代和业务的堆积,安装包会越来越大,安装包变大也带来了其他隐患,比如之前在App Store下载的应用超过150MB时,是无法使用流量下载的,只能连接到无线局域网下载。虽然现在可以设置允许所有App使用蜂窝数据下载,但是有个“超过200MB时请求许可”的选项可能是默认勾选的,而且对流量和手机存储空间敏感的用户,有时候安装包的大小就决定了是否拥有这个用户,所以,对于安装包的瘦身是App发展和优化过程中不可避免的重要一步。
iOS的安装包就是ipa,把一个ipa文件将后缀.ipa修改为.zip,然后将其解压出来,在Payload中的.app 选择显示包内容,就可以查看里面的资源文件。
.app 里面的主要内容
_CodeSignature:存放文件的 hash 列表。里面有一个文件 CodeResources ,这个文件是一个属性列表,包含 bundle 中所有其他文件的列表。这个属性列表只有一项 files,这是一个字典,键是文件名,值通常是 Base64 格式的散列值。如果键表示的文件是可选的,那么值本身也是一个字典,这个字典有一个 hash 键和一个 optional 键(布尔值 true)。它的作用是用来判断一个应用程序是否完好无损,能够防止不小心修改或损坏资源文件。
资源瘦身主要是去掉无用资源
和压缩资源,
资源包括图片
、音视频文件
、配置文件等,其中图片资源应该是用的最多的。
无用资源是指资源在工程文件里,但没有被代码引用。
相关业务或模块移除后,对应的图片资源没有删除,造成安装包里面积累很多无用图片,清理的办法也比较简单,就是根据图片的名字在Xcode里全局搜索,如果没有搜到可能就是没有用到的图片,但是并不完全肯定,也有代码里有可能是通过字符串拼接的方式使用了图片。
查找并删除这些无用的图片资源流程是这样的
推荐一个好用的开源工具 LSUnusedResources,使用起来很简单,搜索速度和匹配的准确性也很高。
除了图片资源外,音视频资源,像.json、plist、README.md 等这样的无用文件也可以删除掉。
对于有用的资源文件,可以通过无损压缩减少占用空间。尽量使用8-bit的PNG图片,比32-bit的图片能减少4倍的压缩率。由于8-bit的图片支持最多256种不同的颜色,所以8-bit的图片一般只应该用于一小部分的颜色图片。例如灰度图片最好使用8-bit。
工程中的图片资源主要有两个方式管理,一种是在项目中添加文件夹存放,另一种是放在Assets.xcassets
管理。推荐使用Assets.xcassets
管理,因为它会把里边的所有 png
格式的图片打包成一个Assets.car文件,压缩比率比文件夹管理图片要高,而且可以根据不同的设备,不同的分辨率设置相应的图片。
有些非必要的文件资源可以放在服务器,结合本地缓存策略,比如主题、皮肤、音乐这样的资源。
App 安装包主要是由资源文件和可执行文件(Mach-O)组成的,可执行文件大小是由代码量决定的。通常情况下,对可执行文件进行瘦身,就是找到并删除无用代码的过程。方法和图片资源清理类似:先找出方法和类的全集,再找到使用过的方法和类,取二者的差集就得到无用代码,人工二次确认后删除。
Xcode build产生的Link Map文件能比较直观的反映出程序各部分的文件大小情况,对于减少包体积很有帮助。
获取 LinkMap :将 Build Setting 里的 Write Link Map File 设置为 Yes,然后指定 Path to Link Map File 的路径就可以得到每次编译后的 LinkMap 文件了。我们只修改一下生成的 Link Map文件的路径就可以了,后缀名不要修改。
LinkMap 文件分为三部分:Object files、Section 和 Symbols。
中的文件
编号,第四列Name 是文件名。可以看到黄色框框部分跟上图Object files 是对应的。通过对 LinkMap 的分析,我们不但可以统计出所有的方法和类,还能够清晰地看到代码所占包大小的具体分布,从而有针对性地对代码进行优化。对Symbols的分析还可以通过方法的二进制重排来提升冷启动速度(本篇不做过多介绍)。
Mach-O是Mach Object的缩写,是Mac/iOS上用于存储程序、库的标准格式。常见的Mach-O文件比如iOS开发好的代码打包好后就是Mach-O格式的文件。Xcode编译完工程会生成一个可执行程序,查找方式也简单,首先在Xcode的Preferences里找到这个路径:
在这个文件夹下找到对应的工程文件名/Build/Products/Debug,进入这个目录下,就可以找到我们的可执行文件了。
iOS 的方法都会通过 objc_msgSend 来调用。而objc_msgSend 在 Mach-O 文件里是通过 __objc_selrefs 这个 section 来获取 selector 这个参数的。所以,__objc_selrefs 里是被调用了的方法,__objc_classrefs 里是被调用过的类,__objc_superrefs 是调用过 super 的类。通过 __objc_classrefs 和 __objc_superrefs,我们就可以找出使用过的类和子类。可以通过MachOView来查看 Mach-O 文件里的信息。
对比Link Map文件就可以找出没有用到的类和方法了,但是Objective-C 是动态语言,方法可以在运行时动态调用,通过这种方法找到的无用代码还需人工确认才可以删除。
如果不想这么麻烦地去分析对比,推荐一个好用的工具 AppCode,代码量不是很大时,AppCode通过静态分析可以快速帮我们找出没有用到的文件和方法,使用方式是在 AppCode 里选择 Code->Inspect Code 就可以进行静态分析。
分析结果也是清晰明了,包括:
Not implemented methods:没有实现的方法;
Key value coding:KVC相关,比如使用KVC访问了@private修饰的成员变量;
Unused class:没有用到的类;
Unused import statement:无用类引入声明;
Unused instance variable :无用的实例变量;
Unused method:没有用到的方法;
Unused property :没有用到的属性;
Unused parameter :无用参数;
Unused local variable :无用的局部变量;
Unused value :无用的值;
Unused macro :无用的宏;
Unused global declaration :无用全局声明。
但是检测结果并不是完全可靠,只是为我们优化代码提供参考方向,最好还是人工确认一下,不要完全相信工具。
Xcode 支持编译器层面的一些优化优化选项,可以让我们在更快的编译速度、更小的二进制和更快的执行速度之间自由选择想要的优化程度。
业务的不断堆积迭代,总会产生一些无用的资源,所以安装包瘦身要定期清理这些无用文件和代码。除了自己写的代码外对用到的三方库也可以做检查优化,比如有的库还支持Mac的i386和x86_64架构处理器,这部分代码就可以根据实际情况删除;对比SDK和三方库的导入对ipa大小的影响,使用满足需求的较小库;项目中重复方法抽离出来等,这些优化可能对安装包的大小影响很小,但是积少成多,形成对ipa大小敏感的习惯也能帮助我们写出效率更高的代码。对安装包瘦身的探索还有很长的路走,本文也只是列举了一些常用的瘦身方案,对于庞大的项目,操作起来也是如履薄冰,还好在没有影响业务和正常功能使用的情况下,我们的ipa减少了大约15MB,优化之路还在继续,以后还会补充更多的方案。