ipa 體積壓縮

減小ipa體積之刪除frameWork中無用machO文件

作者:阿里移動安全  來源:IT165收集  發佈日期:2016-04-14 21:28:02

最近項目末期, 我們團隊爲了ipa的大小使用不少的體積減小的方法, 除了一些常規的方法之外, 我分享一下自己研究出來的新思路。

首先我們來簡單的介紹一下mach-O。

什麼是mach-O?

Mach-O格式全稱爲Mach Object文件格式的縮寫,是mac上可執行文件的格式,類似於windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)。

上面第一個圖是蘋果給出的mach-O格式的示意圖,而第二個圖是我們使用machOView來分析某個可執行文件中的armv7的格式。可以看出他們兩者的關係是對應的。

在machO這其中包含了很多的有效的信息,包括字符串,代碼段,oc類,oc協議等各種的信息,利用這些信息我們也做到分析代碼或者程序邏輯的作用,比如,下面這個數據就是我從這個machO文件裏面導出來的,獲取到了某個framework一個OC類中的所有基本元素。

那什麼又是FatFile/FatBinary

簡單來說,就是一個由不同的編譯架構後的Mach-O產物所合成的集合體。例如上面我就只截取armv7的Mach-O格式座位示例, 而實際上常用的還有arm64/x86_64/i386等格式。

而實際上,包括我們使用的那些framework,大多數也是的。比如下圖我們繼續用machOView分析一下。 

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

FrameWork跟最終可執行文件的區別在哪裏?

這裏我們先隨便寫一個簡單的framework, 在這個framework中我們實現了兩個oc類,如下圖所示:

緊接着我們乾淨用machOView來看看這個新鮮出爐的framework,可以看到該FrameWork在armv7的格式下,裏面存在多個.o文件。

如果我們選擇其中一個繼續看看的話,你就會看到一個完整的Mach-O格式的文件。

由此我們可以得知frameWork也是另外一種情況的Mach-O集合體,是由多個不同的子mach-O文件所組合而成的,他們可以單獨的拆開,而可執行文件則把同一架構下的所有Mach-O文件都進行了合併,他們不能拆開,如果想要更加清晰的定義的話,可以去研究一下蘋果的定義,這裏不做過多的闡述。

我們能做什麼?

可能到這裏你還有點亂,沒關係,我們直接來拆開一個framework給大家看看!

到了這一步,我們就已經知道了我們能把FrameWork中的各個子Mach-O文件拆開, 那麼我們能不能把這些Mach-O文件中有效的部分重新組裝一下, 生成新的一個FrameWork呢?

這必須是可以的,但是其實最重要的關鍵是在於怎麼去確定這個Mach-O文件有沒有被我們的程序使用到。

怎麼確定一個Mach-O有沒有被使用到?

我們直接再來一個簡單的demo,嘗試一下

此時進行編譯

成功.....

然後拆分老的framework文件, 刪掉拆分得到的MachOClassA, 並且用剩下的mach-O文件合併生成一個新的framework, 替換到工程中去並進行編譯

你沒看錯, 他確實是失敗了, 如果在程序中代碼直接使用了某些類或者某些方法, 而其mach-O文件不存在的情況下, 會導致編譯不過(找不到對應的方法), 這也就是說, 我們能夠使用最簡單粗暴的方法來判斷這個machO文件是不是被需要的!

不過, 需要注意的是, category的實現方式是不一樣的,故如果我們刪除了category的方法, 但是直接把Mach-o刪除的話, 編譯時是不會報錯的。有興趣的同學可以自己去看看oc中關於category的實現。

另外還有在程序運行中動態使用的performselector方法(可以通過查詢字符串列表排除)。

下面是相關的流程圖。

怎麼把Mach-O的刪除工具應用到XCode中去

現在我們已經通過編譯的手段獲得了一堆mach-O文件, 但是很多都是pod中引進的, 這個時候我們需要在代碼編譯器執行刪除.o文件的腳本 剛好Xcode確實有這麼一個地方可以設置

成果

在debug模式下大概減少了0.5M, 實際二進制文件減小大概1.2M, 如果計算到最終提交到蘋果並且經過DRM加密後, 預計可以減小1M左右。

PS

category是需要過濾的, 這貨有點特別 刪除找出來的.o文件之後, 可能會引起一些特殊的情況, 當然一般是crash, 因爲有一些特別的代碼他們用法並不是直接引用某個方法, 而是通過NSString相關的方法來獲得Sel或者Class 把查找.o文件的操作放在本地, 而在編譯器上進行編譯的時候就直接執行刪除, 不佔用編譯器的時間(我們的項目要使用六個小時以上的時間來進行查找) 建議進行操作再跑一遍迴歸測試, 確保各個功能模塊正常

其他實踐與猜想以及做過的嘗試

1、.o文件其實是可以直接引進到工程裏面直接編譯的, 也就是說其實可以把frameWork拆開, 然後加到工程中, 一樣能夠正常使用

2、一開始其實是想把.o文件中__text段中無用的函數進行刪除, 但發現流程過於複雜, 而暫時放棄, 查找程序中無用函數的方法以後有機會再進行分享(如果這個成功的話, 估計會減小至少3~4M左右的ipa大小), 附上查找到的程序中無用方法結果的示例

3、其實看了上面那種方法之後, 我們緊接着又能想到, 暴力的將.m文件中的代碼刪除, 然後看看哪些工程中可見的代碼是可以刪除的(ps. 主要針對非framework, 另外也同樣需要注意category以及performselector的問題, 需要配合查找字符串列表一起進行, 或者手工進行判斷)。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章