前言
隨著項目的進(jìn)行,iOS 端的 framework文件越來越大,已經(jīng)有些客戶開始抱怨了,iOS 端的 framework瘦身也就相應(yīng)的提上了日程,這里對我們 Framework 瘦身進(jìn)行了一個小結(jié)。
1. 精簡代碼
這一步效果不是很大,主要對頭文件的引用、重復(fù)代碼、無用代碼等進(jìn)行了梳理。
2. 編譯優(yōu)化
2.1 優(yōu)化ROI(Optimization Level)
XCode編譯優(yōu)化選項中,Optimization Level效果最明顯,建議所有模塊構(gòu)建都開啟Oz選項。如果APP有動態(tài)庫,并且依賴了openssl等基礎(chǔ)靜態(tài)庫,建議和APP工程共享,并通過EXPORTED_SYMBOLS_FILE的選型,確保動態(tài)庫中需要用到的符號都在編譯過程保留。
項目中 Release 模式配置成 [-OZ],其他模式配置成Fastest,Smallest[-OS]
2.2 LTO 配置
Link-Time Optimization 是 LLVM 編譯器的一個特性,用于在 link中間代碼時,對全局代碼進(jìn)行優(yōu)化。這個優(yōu)化是自動完成的,因此不需要修改現(xiàn)有的代碼;這個優(yōu)化也是高效的,因為可以在全局視角下優(yōu)化代碼。
它的優(yōu)化主要體現(xiàn)在如下幾個方面:
1.多余代碼去除(Dead code elimination):如果一段代碼分布在多個文件中,但是從來沒有被使用,普通的 -O3 優(yōu)化方法不能發(fā)現(xiàn)跨中間代碼文件的多余代碼,因此是一個“局部優(yōu)化”。但是 Link-Time Optimization 技術(shù)可以在 link 時發(fā)現(xiàn)跨中間代碼文件的多余代碼;
- 跨過程優(yōu)化(Interprocedural analysis and optimization):這是一個相對廣泛的概念。舉個例子來說,如果一個 if 方法的某個分支永不可能執(zhí)行,那么在最后生成的二進(jìn)制文件中就不應(yīng)該有這個分支的代碼;
3.內(nèi)聯(lián)優(yōu)化(Inlining optimization):內(nèi)聯(lián)優(yōu)化形象來說,就是在匯編中不使用 “call func_name ” 語句,直接將外部方法內(nèi)的語句“復(fù)制”到調(diào)用者的代碼段內(nèi)。這樣做的好處是不用進(jìn)行調(diào)用函數(shù)前的壓棧、調(diào)用函數(shù)后的出棧操作,提高運(yùn)行效率與??臻g利用率。
同時也有以下幾個缺點(diǎn):
1.降低編譯鏈接速度,只建議在打正式包時開啟
2.降低 link map 可讀性(出現(xiàn)XX-lto.thin的類)
Link-Time Optimization 選項在 Release模式下配置成 Incremental,其他模式配置成 No
2.3 Strip Linked Product
如果為 YES,則進(jìn)行裁剪(在編譯最后對 framework 進(jìn)行 strip));如果為NO,則不進(jìn)行裁剪;至于裁剪什么級別的符號由 Strip Style 配置決定;如果Deployment Postprocessing為NO,Strip Linked Product設(shè)置無效;
Strip Linked Product 選項在 Release模式下配置成 YES,其他模式配置成 No
2.4 Deployment Postprocessing
如果為 YES,在編譯生成目標(biāo)文件之后要進(jìn)行后續(xù)處理;如果為 NO,則不會有后續(xù)處理;使用 Xcode Archive 進(jìn)行編譯,Deloyment Postprocessing 的值恒為YES;
Deployment Postprocessing 選項在 Release模式下配置成 YES,其他模式配置成 No
2.5 Strip Style
- Debugging Symbols :會將調(diào)試符號從二進(jìn)制中刪除掉,即去除 DWARF 信息;
- Non-Global Symbols :會將局部符號和調(diào)試符號從二進(jìn)制中刪除掉,即去除 DWARF 信息以及部分 Symbol Table 中的信息;
- All Symbols :去除全部符號,即去除 DWARF 中的調(diào)試信息以及Symbol Table 中目標(biāo)模塊定義的全局、局部符號信息。
Strip Style 選項在 Release模式下配置成 Non-Global Symbol,其他模式配置成 Debugging Symbols
2.6 Symbols Hidden by Default
這是全局的開關(guān),用來設(shè)置符號的默認(rèn)可見性,設(shè)置為YES,會把所有符號都定義成”private extern”;
也可以可以使用編譯器屬性attribute((visibility("default")))和attribute((visibility("hidden")))來控制符號的可見性;
__attribute__((visibility("default"))) void MyFunction1() {} //可見
__attribute__((visibility("hidden"))) void MyFunction2() {} //不可見
Symbols Hidden by Default 選項在 Release模式下配置成 YES,其他模式配置成 NO
設(shè)置 Symbols Hidden by Default為 YES 后會默認(rèn) OC 的類是 hidden的,因此需要將對外暴露的類用__attribute__((visibility("default"))) 修飾
2.7 Dead Code Stripping
Dead Code Stripping開啟后會在鏈接時移除未使用的代碼,它對靜態(tài)語言C/C++/Swift有效,對動態(tài)語言O(shè)C無效。
Dead Code Stripping 選項在 Release模式下配置成 YES,其他模式配置成 NO
2.8 Generate Debug Symbols
是否生成符號文件,設(shè)置為NO;這樣才能支持?jǐn)帱c(diǎn)調(diào)試;注意Debug模式下,Deployment Postprocessing 一定要NO,否則Generate Debug Symbols的設(shè)置了YES,也不支持?jǐn)帱c(diǎn)調(diào)試;
Generate Debug Symbols 選項在 Release模式下配置成 YES,其他模式配置成 NO
2.8 C++去掉RTTI支持
Eable C++ Runtime Types 選項在 Release模式下配置成 NO,其他模式配置成 YES
同時還需要在 OTHER_CFLAGS里面添加-fno-rtti
2.9 C++只導(dǎo)出必要符號:Symbol Visibility
OTHER_CFLAGS中添加-fvisibility=hidden
需要導(dǎo)出的符號 設(shè)置需要導(dǎo)出的符號
__attribute__((visibility("default"))) void MyFunction1() {}
__attribute__((visibility("default"))) void MyFunction2() {}
2.10 Strip Debug Symbols During Copy
Strip Debug Symbols During Copy 選項在 Release模式下配置成 YES,其他模式配置成 NO
2.11 Enable BitCode
Enable BitCode 關(guān)掉后編譯出來的 framework 會更小,目前考慮大多數(shù)項目都是打開的,配置打開狀態(tài)
2.12 Mach-O Type
Mach-O Type 設(shè)置成 Dynamic Library , 動態(tài)的framework是允許的上線的不會拒絕, 設(shè)置成Dynamic Library后能解決掉大多數(shù)由于依賴相同第三方庫而引起的符號沖突問題。包也會比靜態(tài) framework 小很多
2.13 Exported Symbols File
EXPORTED_SYMBOLS_FILE 可以傳入一個符號表白名單列表,在這個名單里面的符號符號會暴露給外部,不在該名單的的符號會隱藏。
- 如何看 iOS 動態(tài)/靜態(tài)庫的符號表,可以去了解下
nm命令
寫在最后
通過上面步驟對我們的 SDK 項目進(jìn)行瘦身,將arm64和 armv7兩個架構(gòu)包的總大小降低30%和同行 SDK 包大小基本一致。