首先我們來簡單的介紹一下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類中的所有基本元素。
簡單來說,就是一個由不同的編譯架構後的mach-o産物所合成的集合體。例如上面我就隻截取armv7的mach-o格式座位示例, 而實際上常用的還有arm64/x86_64/i386等格式。
而實際上,包括我們使用的那些framework,大多數也是的。比如下圖我們繼續用machoview分析一下。
可以看到arm64/armv7架構的存在。
這裡我們先随便寫一個簡單的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檔案有沒有被我們的程式使用到。
我們直接再來一個簡單的demo,嘗試一下
此時進行編譯
成功.....
然後拆分老的framework檔案, 删掉拆分得到的machoclassa, 并且用剩下的mach-o檔案合并生成一個新的framework, 替換到工程中去并進行編譯
你沒看錯, 他确實是失敗了, 如果在程式中代碼直接使用了某些類或者某些方法, 而其mach-o檔案不存在的情況下, 會導緻編譯不過(找不到對應的方法), 這也就是說, 我們能夠使用最簡單粗暴的方法來判斷這個macho檔案是不是被需要的!
不過, 需要注意的是, category的實作方式是不一樣的,故如果我們删除了category的方法, 但是直接把mach-o删除的話, 編譯時是不會報錯的。有興趣的同學可以自己去看看oc中關于category的實作。
另外還有在程式運作中動态使用的performselector方法(可以通過查詢字元串清單排除)。
下面是相關的流程圖。
現在我們已經通過編譯的手段獲得了一堆mach-o檔案, 但是很多都是pod中引進的, 這個時候我們需要在代碼編譯器執行删除.o檔案的腳本 剛好xcode确實有這麼一個地方可以設定
在debug模式下大概減少了0.5m, 實際二進制檔案減小大概1.2m, 如果計算到最終送出到蘋果并且經過drm加密後, 預計可以減小1m左右。
category是需要過濾的, 這貨有點特别
删除找出來的.o檔案之後, 可能會引起一些特殊的情況, 當然一般是crash, 因為有一些特别的代碼他們用法并不是直接引用某個方法, 而是通過nsstring相關的方法來獲得sel或者class
把查找.o檔案的操作放在本地, 而在編譯器上進行編譯的時候就直接執行删除, 不占用編譯器的時間(我們的項目要使用六個小時以上的時間來進行查找)
建議進行操作再跑一遍回歸測試, 確定各個功能子產品正常
.o檔案其實是可以直接引進到工程裡面直接編譯的, 也就是說其實可以把framework拆開, 然後加到工程中, 一樣能夠正常使用
一開始其實是想把.o檔案中__text段中無用的函數進行删除, 但發現流程過于複雜, 而暫時放棄, 查找程式中無用函數的方法以後有機會再進行分享(如果這個成功的話, 估計會減小至少3~4m左右的ipa大小), 附上查找到的程式中無用方法結果的示例
其實看了上面那種方法之後, 我們緊接着又能想到, 暴力的将.m檔案中的代碼删除, 然後看看哪些工程中可見的代碼是可以删除的(ps. 主要針對非framework, 另外也同樣需要注意category以及performselector的問題, 需要配合查找字元串清單一起進行,
或者手工進行判斷)。