iOS瘦身之刪除FrameWork中無用mach-O文件
最近項目末期, 我們團隊為了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文件的操作放在本地, 而在編譯器上進行編譯的時候就直接執行刪除, 不占用編譯器的時間(我們的項目要使用六個小時以上的時間來進行查找)
建議進行操作再跑一遍回歸測試, 確保各個功能模塊正常
其他實踐與猜想以及做過的嘗試
.o文件其實是可以直接引進到工程里面直接編譯的, 也就是說其實可以把frameWork拆開, 然后加到工程中, 一樣能夠正常使用
一開始其實是想把.o文件中__text段中無用的函數進行刪除, 但發現流程過于復雜, 而暫時放棄, 查找程序中無用函數的方法以后有機會再進行分享(如果這個成功的話, 估計會減小至少3~4M左右的ipa大小), 附上查找到的程序中無用方法結果的示例
(點擊放大圖像)
其實看了上面那種方法之后, 我們緊接著又能想到, 暴力的將.m文件中的代碼刪除, 然后看看哪些工程中可見的代碼是可以刪除的(ps. 主要針對非framework, 另外也同樣需要注意category以及performselector的問題, 需要配合查找字符串列表一起進行, 或者手工進行判斷)。
來自: http://www.infoq.com/cn/articles/ios-thinning-delete-unnecessary-mach-o