0、iOS 包的构成
在 iPhone 上的应用包称为 ipa,即 iPhone Application。将 ipa 包解压后主要由以下三部分构成:
1)同名的可执行文件;
2)Asset.car、.nib、.bundle、Localizable.strings 等资源文件;
3)其他:_CodeSignature 文件夹,签名信息。
在iOS(和Mac OS X)上,主要的可执行文件格式是 Mach-O 格式。在 Windows 上的可直接执行的文件扩展名是 .exe,而在Linux(以及很多版本的Unix)系统上 ELF 是可直接执行的文件格式。
Mach-O 简介
Mach 内核被 NeXT 公司的 NeXTSTEP 操作系统使用。在 Mach 上,一种可执行的文件格是就是 Mach-O(Mach Object file format)。1996 年,乔布斯将 NeXTSTEP 带回苹果,成为了 OS X 的内核基础。所以虽然 Mac OS X 是 Unix 的“后代”,但所主要支持的可执行文件格式是Mach-O。
iOS 是从 OS X 演变而来,所以同样是支持 Mach-O 格式的可执行文件。
Mach-O 的组成
![](https://img.haomeiwen.com/i7179440/90327a5c52725c1d.png)
Mach-O 文件由三部分组成:
1)头部 (Header): Mach-O 文件的架构 比如 Mac 的 PPC, PPC64, IA-32, x86-64,iOS 的 arm系列。
2)加载命令(Load commands): 在虚拟内存中指定文件的逻辑结构和布局。
3)原始段数据(Raw segment data):可以拥有多个段(segment),每个段可以拥有零个或多个区域(section)。每一个段(segment)都拥有一段虚拟地址映射到进程的地址空间。
1、段表
段表,描述各个段在最后编译成的可执行文件中的偏移位置及大小,包括了
代码段(__TEXT,保存程序代码段编译后的机器码)和
数据段(__DATA,保存变量值)
![](https://img.haomeiwen.com/i7179440/189d9f4463d0a0a9.png)
第二行 __symbol_stub 的地址 0x00275FD0 = 第一行 __text 的地址 0x00002740 + 大小0x00273890
段名称的含义:
__text:编译后的程序执行语句
__data:已初始化的全局变量和局部静态变量
__bss:未初始化的全局变量和局部静态变量
__cstring:代码里的字符串常量
优化方案
![](https://img.haomeiwen.com/i7179440/402d229779e65b40.png)
1、无用代码
在项目里新建一个类,给它添加几个方法,但不要在任何地方 import 它,build 完项目后观察linkmap,你会发现这个类还是被编译进可执行文件了。
按 C++ 的经验,没有被使用到的类和方法编译器都会优化掉,不会编进最终的可执行文件,但OC 不一样,因为 OC 的动态特性,它可以通过类和方法名反射获得这个类和方法进行调用(KVC),所以就算在代码里某个类没被使用到,编译器也没法保证这个类不会在运行时通过反射去调用,所以只要是在项目里的文件,无论是否又被使用到都会被编译进可执行文件。
对此我们可以通过脚本,遍历整个项目的文件,找出所有没有被引用的类文件和没有被调用的方法,在保证没有其他地方动态调用的情况下把它们去掉。如果整个项目历时很长,历时代码遗留较多,这个清理对可执行文件省出的空间还是挺可观的。
2、类/方法名长度
观察 linkmap 可以发现每个类和方法名都在 __cstring 段里都存了相应的字符串值,所以类和方法名的长短也是对可执行文件大小是有影响的,原因还是 OC 的动态特性,因为需要通过类/方法名反射找到这个类/方法进行调用(KVC 机制),OC 对象模型会把类/方法名字符串都保存下来。
对此我们可以考虑在编译前把所有类和方法名进行混淆,跟压缩 js 一样,把长名字替换成短名字,这样做的好处除了缩小体积外,还对安全性有很大提升,别人拿到可执行文件对它 class-dump 出来的结果都是混淆后的类和方法名,就无法从类和方法名中猜出某个方法是做什么的,就难以挂钩子进行 hack。不过这样做有个缺点,就是 crash 堆栈反解出来的堆栈方法名会是混淆后的,需要再加一层混淆->原名的转换,实现和使用成本有点高。
实际上这部分占用的长度比较小,中型项目也就几百K,对安全性要求高的情况可以试试。
3、静态字符串抽离形成静态文件
代码上定义的所有静态字符串都会记录在在可执行文件的 __cstring 段,如果项目里 Log 非常多,这个空间占用也是可观的,也有几百K的大小,可以考虑清理所有冗余的字符串。另外如果有特别长的字符串,建议抽离保存成静态文件,因为 AppStore 对可执行文件加密导致压缩率低,特别长的字符串抽离成静态资源文件后压缩率会比在可执行文件里高很多。
参考文档:https://www.jianshu.com/p/4bd6d1315104
网友评论