iOS app瘦身优化之路

缩减iOS安装包大小是很多中大型APP都要做的事,一般首先会对资源文件下手,压缩图片/音频,去除不必要的资源。这些资源优化做完后,我们还可以尝试对可执行文件进行瘦身,项目越大,可执行文件占用的体积越大,又因为AppStore会对可执行文件加密,导致可执行文件的压缩率低,压缩后可执行文件占整个APP安装包的体积比例大约有80%~90%,还是挺值得优化的。

瘦身的第一步

对于app瘦身之前做过图片优化,采用脚本的方式把一些在pods中的图片迁移至imageSet下,但是这样的优化是有瓶颈的,因为它受制于图片的多少、图片在pods下的存储方式,而由于pods之间的依赖关系复杂以及有些pods中的图片为了避免全局重复问题采用resource_bundle方式来管理,这样对于图片优化极为不利,所以可以优化的空间还是有上限。这是瘦身的第一步,现在已经完成了,并且对部分采用resource_bundle方式的以的pods加以修改。这个可以参考这篇文章:点击打开链接

瘦身的第二步

考虑到整个app依赖pods多达119个,可想而之就是这些依赖是不是都全部用上了,这个是有些疑惑的,如果能够剔掉一部分无用的依赖pods,这对于瘦身也是大有益处的。但是不幸的是,在网上怎么查找也没有找到相关工具可以检测到哪些是无用的pods,这样扔下一个难题,只好采用最笨的方式去挨个手动查找了。首先去podfile里面找那些没有被依赖的pods,然后去项目中找到这些pods,然后通过找到使用这些pods的入口类,然后在在全局搜索这个类,如果有其他代码使用这个类,那么这个pod是有用的,如果找不到其他任何代码对些有引用,那可以判断这个pod是无用依赖,这样可以将此pod进行踢除。难点在于如何快速确定一个pod的入口类,这个是比较难确定。但是如果去掉的pods能通过编译并且能正常安装和运行,那说明这个pods肯定是无用的。所以根据这一操作,现在粗略去掉了六个pods以及大象相关的pods,这项工作需要比较细心,而且耗费时间,但是也是一种可行方式。现在项目所有依赖库个数减少到了97个。

瘦身的第三步

从编译选项着手,编译选项方面又可以分为下面两个方面:

1.编译器优化级别

Build Settings->Optimization Level有几个编译优化选项,release版应该选择Fastest, Smalllest,这个选项会开启那些不增加代码大小的全部优化,并让可执行文件尽可能小。

2.去除符号信息

Strip Debug Symbols During Copy 和 Symbols Hidden by Default 在release版本应该设为yes,可以去除不必要的调试符号。Symbols Hidden by Default会把所有符号都定义成”private extern”,具体意思和作用我还不清楚,有待研究,但设了后会减小体积。这些选项目前都是XCode默认选项,但旧版XCode生成的项目可能不是,可以检查一下。

其他优化还可以参考苹果的官方文档—CodeFootprint.pdf

瘦身的第四步

项目里会引入很多第三方静态库,如果能知道这些第三方库在可执行文件里占用的大小,就可以评估是否值得去找替代方案去掉这个第三方库。我们可以从linkmap中统计出这个信息,写了个node.js脚本,可以通过linkmap统计每个.o目标文件占用的体积和每个.a静态库占用的体积,并进行排序。

不过在执行这段nodejs代码时首先需要你找到编译后的可执文件信息存储文件,下面介绍如何开启并找到这个.txt文件:

1.XCode开启编译选项Write Link Map File
XCode -> Project -> Build Settings -> 搜map -> 把Write Link Map File选项设为yes,并指定好linkMap的存储位置

 

2.编译后,到编译目录里找到该txt文件,文件名和路径就是上述的Path to Link Map File
文件位于~/Users/yuzhuo/Library/Developer/Xcode/DerivedData/Jovi-dgmwdpjofxrufihidpjqqtomnphq/Build/Intermediates/Jovi.build/Debug-iphonesimulator/Jovi.build/Objects-normal/x86_64

这个LinkMap里展示了整个可执行文件的全貌,列出了编译后的每一个.o目标文件的信息(包括静态链接库.a里的),以及每一个目标文件的代码段,数据段存储详情。

这个文件可以让你了解整个APP编译后的情况,也许从中可以发现一些异常,还可以用这个文件计算静态链接库在项目里占的大小,有时候我们在项目里链了很多第三方库,导致APP体积变大很多,我们想确切知道每个库占用了多大空间,可以给我们优化提供方向。LinkMap里有了每个目标文件每个方法每个数据的占用大小数据,所以只要写个脚本,就可以统计出每个.o最后的大小,属于一个.a静态链接库的.o加起来,就是这个库在APP里占用的空间大小。

nodejs代码详见这里(需)。

执行脚本的命令行:node linkmap.js filepath -hl下面是我在自己上电脑执行的命令:

node linkmap.js /Users/yuzhuo/Desktop/DPMerchant-LinkMap-normal-x86_64/DPMerchant-LinkMap-normal-x86_64.txt -hl

执行脚本后的结果如下图:(太多了只给出部分的截图,全部结果放在txt文档中)


得到了这个结果可以对一些占用比较大的lib以及代码进行针对性优化了。

瘦身的第五步

删除无用代码。在项目里新建一个类,给它添加几个方法,但不要在任何地方import它,build完项目后观察linkmap,你会发现这个类还是被编译进可执行文件了。

按C++的经验,没有被使用到的类和方法编译器都会优化掉,不会编进最终的可执行文件,但object-c不一样,因为object-c的动态特性,它可以通过类和方法名反射获得这个类和方法进行调用,所以就算在代码里某个类没被使用到,编译器也没法保证这个类不会在运行时通过反射去调用,所以只要是在项目里的文件,无论是否又被使用到都会被编译进可执行文件。

对此我们可以通过脚本,遍历整个项目的文件,找出所有没有被引用的类文件和没有被调用的方法,在保证没有其他地方动态调用的情况下把它们去掉。如果整个项目历时很长,历时代码遗留较多,这个清理对可执行文件省出的空间还是挺可观的。

删除无用图片资源。在github上面发现有个开源项目Unused,通过运行这个项目可以发现无用的图片资源,并导出无用资源路径的txt文件,再通过写脚本对这些资源进行删除,下面是这个项目的运行界面。LSUnusedResources 


删除无用图片资源的脚本如下图:


后续

cocoapods为每个target生成一个Pods-xxx-resources.sh的资源处理脚本,这个脚本在编译时会把项目中(Pods、Nova、Targets)所有通过xcassets管理的图片重新copy到一起导致Nova的DPScope下有一份,而DPMessage作为一个target独立存在也会存一份。(Nova下xcassets名为Images.xcassets,Pods 和其余Targets下xcassets名为Assets.xcassets)

解决方法

针对本次问题,我们在pod install跑完以后修改cocoapods的资源处理脚本,仅处理Pods和Nova中Image.xcassets管理的图片,排除掉其它Target中的图片,让各个Target独立管理。

Pods-Jovi-resources.sh脚本修改如下


这里在第4行,只需要将Jovi下名为 Images.xcassets图片管理的加入XCASSET_FILES即可,这样保证其余Targets的图片不会重复打包。

由于我们点评管家项目中只有一个target所以这种问题暂时不存在,不过这样可以避免以后出现这种问题。

修改脚本如下:



最终优化结果:

采用这么多方式去瘦身后发现效果还是喜人的,未瘦身前安装到ipone 6s上v5.1.1的大小为66.9M,减小到现在的33.4M,整体缩减33.5M,缩减率为50%,而安装包.ipa文件从开始的大小23.8M缩减到现在的14.8M,缩减率也能达到38%



你可能感兴趣的:(ios)