原文: https://sanwen8.cn/p/5942YDZ.html
近来我们一直在做 ipa 包大小的缩减,在删除了无用图片,缩减项目中图片的体积,取得了较大的效果。但是成果背后的问题也接踵而至,删除完了图片,压缩完了图片大小之后,我们应该怎么来减少 ipa 包大小呢?从哪里去减小、减小什么、怎么减小呢?
带着这样的问题,我们先来了解一下 ipa包的构成:
ipa包主要由三大部分构成:
ipa 包解压之后主要由三部门构成:1、可执行文件;2、Asset.car 文件(asset 文件夹中图片的压缩文件);3、其他:_CodeSignature文件夹,签名信息。
我们之前做的事情主要是压缩2和3中的内容,那么接下来我们需要缩小的是1的内容了。
既然想要缩小可执行文件的大小,那么可执行文件是个什么东西呢?
使用 file 命令查看这个可执行文件:
文章图片
这下我们就知道了,这是个 Mach-O文件类型,里面包含了两个架构:armv7&arm64。
Mach-O? Mach-O格式是个啥?是怎么组成的?
我们知道 Windows下的文件都是 PE 文件,同样在 OS X 和iOS中可执行文件是 Mach-O格式的。
Mach-O 的基本组成:
? 头部(header structure)。
? 加载命令(load command)。
? 段(segment)。可以拥有多个段(segment),每个段可以拥有零个或多个区域(section)。每一个段(segment)都拥有一段虚拟地址映射到进程的地址空间。
? 链接信息。一个完整的用户级 Mach-o 文件的末端是链接信息。其中包含了动态加载器用来链接可执行文件或者依赖库所需使用的符号表、字符串表等等。
头部信息和加载命令都是用来描述应用程序类型的,链接信息则是如符号表之类的,所以 Mach-O 中最重要的部分是这些 segment 。
segment是什么呢?来看看普通的编译过程吧:
基本的编译过程分为四个步骤:
- 预处理(Pre-process):把宏替换,删除注释,展开头文件,产生 .i 文件。
- 编译(Compliling):使用预处理后的单词构建词法树,生成语法树输出 AST(Abstract Syntax Tree),将 AST 转化为更低级的中间码(LLVM IR),优化中间代码生成输出汇编代码,把之前的 .i 文件转换为汇编语言,产生 .s文件。
- 汇编(Asembly):把汇编语言文件转换为机器码文件,产生 .o 文件。
- 链接(Link):对 .o 文件中的对于其他的库的引用的地方进行引用,生成最后的可执行文件(同时也包括多个.o文件进行 link)。
那么我们知道了 Mach-O 文件是一堆 segment 组成的,而可执行程序是由 .o文件 link 而成的,那么这些 .o 文件应该能反映 segment的大小,也能反应可执行程序的大小。
好,我们现在的目标变成了去看 .o文件的大小,那么现在让我们来看看 linkMap。
linkMap打开是个啥?
【LinkMap|LinkMap分析】
Mach-O是编译完成的二进制文件,LinkMap是中间产生的如符号表类的文件,这个LinkMap里展示了整个可执行文件的全貌,列出了编译后的每一个 .o 目标文件的信息(包括静态链接库 .a 里的),以及每一个目标文件的代码段、数据段存储详情。
文章图片
linkMap的结构:
linkMap文件一般有三个部分:
1、在 linkMap 里首先列出来的是目标文件列表:前面中括号里的是这个文件的编号,后面会用到,像项目里引用到静态链接库里的目标文件都会在这里列出来。如编号 [2] 的 B.o 和编号 [5] 的 A.o。
文章图片
2、接着是一个段表,描述各个段在最后编译成的可执行文件中的偏移位置及大小,包括了代码段(TEXT,保存程序代码段编译后的机器码)和数据段(DATA,保存变量值)。首列是数据在文件的偏移位置,第二列是这一段占用大小,第三列是段类型、代码段和数据段,第四列是段名称。这里可以清楚看到各种类型的数据在最终可执行文件里占的比例,例如 text 表示编译后的程序执行语句,data 表示已经初始化的全局变量和局部静态变量,__cstring 表示代码里的字符串常量,等等。
文章图片
每一行的数据都紧跟在上一行后面,如第二行 symbol_stub 的地址 0x100006BD8 就是第一行 text的地址 0×100006A68加上大小 0x00000170,整个可执行文件大致数据分布就是这样。
3、接着就是按上表顺序,列出具体的按每个文件列出每个对应字段的位置和占用空间。同样首列是数据在文件的偏移地址,第二列是占用大小,第三列是所属文件序号,对应上述 Object ?les 列表,最后是名字,例如第51行代表了文件序号为5(反查上面就是A)的 function1A 方法占用了 4byte 大小。
文章图片
linkMap的用法:
根据上面的介绍,我们可以把所有 ?le相同的 size 加起来,就可以得到该文件的大小了,根据文件的前缀名,就可以得到项目中各个独立模块的大小了。还可以对两个迭代得到的 linkmap 文件分析结果进行比较就可以得出,这个迭代相比上个迭代新增了多少文件,减少了多少文件,哪些文件比较大需要减少等信息。
linkMap文件的获取方法:
文章图片
在xcode中设置编译选项:XCode ->Project->Build Settings->搜map->把Write Link Map File 选项设为 yes,并在 Path to Link Map File 中指定好linkMap的存储位置。
编译完成之后,即可在存储位置获得 linkMap文件。
参考资料:
1、build过程:https://www.objccn.io/issue-6-1/。
2、编译过程:https://segmentfault.com/a/119****03****087。
3、clang &LLVM:http://kb.cnblogs.com/page/114879/。
4、.tbd文件:http://www.tuicool.com/articles/Jv2A JvZ、 http://www.cocoachina.com/ios/20160506/16141.html。