你的位置:首页 > 软件开发 > 操作系统 > 减小ipa体积之删除frameWork中无用mach

减小ipa体积之删除frameWork中无用mach

发布时间:2016-04-14 19:00:22
最近项目末期, 我们团队为了ipa的大小使用不少的体积减小的方法, 除了一些常规的方法之外, 我分享一下自己研究出来的新思路。首先我们来简单的介绍一下mach-O。什么是mach-O?Mach-O格式全称为Mach Object文件格式的缩写,是mac上可执行文件的格式,类似于w ...

最近项目末期, 我们团队为了ipa的大小使用不少的体积减小的方法, 除了一些常规的方法之外, 我分享一下自己研究出来的新思路。

首先我们来简单的介绍一下mach-O。

什么是mach-O?

Mach-O格式全称为Mach Object文件格式的缩写,是mac上可执行文件的格式,类似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)。

减小ipa体积之删除frameWork中无用mach

上面第一个图是苹果给出的mach-O格式的示意图,而第二个图是我们使用machOView来分析某个可执行文件中的armv7的格式。可以看出他们两者的关系是对应的。

在machO这其中包含了很多的有效的信息,包括字符串,代码段,oc类,oc协议等各种的信息,利用这些信息我们也做到分析代码或者程序逻辑的作用,比如,下面这个数据就是我从这个machO文件里面导出来的,获取到了某个framework一个OC类中的所有基本元素。

减小ipa体积之删除frameWork中无用mach

那什么又是FatFile/FatBinary

减小ipa体积之删除frameWork中无用mach

简单来说,就是一个由不同的编译架构后的Mach-O产物所合成的集合体。例如上面我就只截取armv7的Mach-O格式座位示例, 而实际上常用的还有arm64/x86_64/i386等格式。

而实际上,包括我们使用的那些framework,大多数也是的。比如下图我们继续用machOView分析一下。 

可以看到arm64/armv7架构的存在。

减小ipa体积之删除frameWork中无用mach

FrameWork跟最终可执行文件的区别在哪里?

这里我们先随便写一个简单的framework, 在这个framework中我们实现了两个oc类,如下图所示:

减小ipa体积之删除frameWork中无用mach

紧接着我们干净用machOView来看看这个新鲜出炉的framework,可以看到该FrameWork在armv7的格式下,里面存在多个.o文件。

减小ipa体积之删除frameWork中无用mach

如果我们选择其中一个继续看看的话,你就会看到一个完整的Mach-O格式的文件。

减小ipa体积之删除frameWork中无用mach

由此我们可以得知frameWork也是另外一种情况的Mach-O集合体,是由多个不同的子mach-O文件所组合而成的,他们可以单独的拆开,而可执行文件则把同一架构下的所有Mach-O文件都进行了合并,他们不能拆开,如果想要更加清晰的定义的话,可以去研究一下苹果的定义,这里不做过多的阐述。

我们能做什么?

可能到这里你还有点乱,没关系,我们直接来拆开一个framework给大家看看!

减小ipa体积之删除frameWork中无用mach

到了这一步,我们就已经知道了我们能把FrameWork中的各个子Mach-O文件拆开, 那么我们能不能把这些Mach-O文件中有效的部分重新组装一下, 生成新的一个FrameWork呢?

这必须是可以的,但是其实最重要的关键是在于怎么去确定这个Mach-O文件有没有被我们的程序使用到。

怎么确定一个Mach-O有没有被使用到?

我们直接再来一个简单的demo,尝试一下

减小ipa体积之删除frameWork中无用mach

此时进行编译

减小ipa体积之删除frameWork中无用mach

成功.....

然后拆分老的framework文件, 删掉拆分得到的MachOClassA, 并且用剩下的mach-O文件合并生成一个新的framework, 替换到工程中去并进行编译

减小ipa体积之删除frameWork中无用mach

你没看错, 他确实是失败了, 如果在程序中代码直接使用了某些类或者某些方法, 而其mach-O文件不存在的情况下, 会导致编译不过(找不到对应的方法), 这也就是说, 我们能够使用最简单粗暴的方法来判断这个machO文件是不是被需要的!

不过, 需要注意的是, category的实现方式是不一样的,故如果我们删除了category的方法, 但是直接把Mach-o删除的话, 编译时是不会报错的。有兴趣的同学可以自己去看看oc中关于category的实现。

另外还有在程序运行中动态使用的performselector方法(可以通过查询字符串列表排除)。

下面是相关的流程图。

减小ipa体积之删除frameWork中无用mach

怎么把Mach-O的删除工具应用到XCode中去

现在我们已经通过编译的手段获得了一堆mach-O文件, 但是很多都是pod中引进的, 这个时候我们需要在代码编译器执行删除.o文件的脚本 刚好Xcode确实有这么一个地方可以设置

减小ipa体积之删除frameWork中无用mach

成果

在debug模式下大概减少了0.5M, 实际二进制文件减小大概1.2M, 如果计算到最终提交到苹果并且经过DRM加密后, 预计可以减小1M左右。

减小ipa体积之删除frameWork中无用mach

PS

  1. category是需要过滤的, 这货有点特别
  2. 删除找出来的.o文件之后, 可能会引起一些特殊的情况, 当然一般是crash, 因为有一些特别的代码他们用法并不是直接引用某个方法, 而是通过NSString相关的方法来获得Sel或者Class
  3. 把查找.o文件的操作放在本地, 而在编译器上进行编译的时候就直接执行删除, 不占用编译器的时间(我们的项目要使用六个小时以上的时间来进行查找)
  4. 建议进行操作再跑一遍回归测试, 确保各个功能模块正常

其他实践与猜想以及做过的尝试

1、.o文件其实是可以直接引进到工程里面直接编译的, 也就是说其实可以把frameWork拆开, 然后加到工程中, 一样能够正常使用

2、一开始其实是想把.o文件中__text段中无用的函数进行删除, 但发现流程过于复杂, 而暂时放弃, 查找程序中无用函数的方法以后有机会再进行分享(如果这个成功的话, 估计会减小至少3~4M左右的ipa大小), 附上查找到的程序中无用方法结果的示例

减小ipa体积之删除frameWork中无用mach

3、其实看了上面那种方法之后, 我们紧接着又能想到, 暴力的将.m文件中的代码删除, 然后看看哪些工程中可见的代码是可以删除的(ps. 主要针对非framework, 另外也同样需要注意category以及performselector的问题, 需要配合查找字符串列表一起进行, 或者手工进行判断)。


作者:靖明@阿里移动安全 ,更多技术文章,请点击这里


原标题:减小ipa体积之删除frameWork中无用mach

关键词:ip

ip
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。