1、Bitcode
-
隨着 Xcode7 的發布,Apple 提供了一項新的技術來支持 App 瘦身功能,那就是 Bitcode。
-
1、BitCode 是什么
-
Bitcode is an intermediate representation of a compiled program. Apps you upload to iTunes Connect that contain bitcode will be compiled and linked on the store. Including bitcode will allow Apple to re-optimize your app binary in the future without the need to submit a new version of your app to the store.
-
Xcode hides symbols generated during build time by default, so they are not readable by Apple. Only if you choose to include symbols when uploading your app to iTunes Connect would the symbols be sent to Apple. You must include symbols to receive crash reports from Apple.
-
上述引自Apple的文檔 App Thinning (iOS, tvOS, watchOS)。
-
其大概意思是 Bitcode 類似於一個中間碼,被上傳到 AppleStore 之后,蘋果會根據下載應用的用戶的手機指令集類型生成只有該指令集的二進制,進行下發,從而達到精簡安裝包體積的目的。
-
-
2、一點編譯原理
-
為了更好的理解什么是 Bitcode,我們簡短的看一下編譯器編譯的過程:
-
Lexer :讀入源文件,並將其轉化成字符流。
-
Parser :將字符流轉換成 AST(抽象語法樹)。
-
Semantic Analysis :對輸入的 AST 進行語法檢查。
-
Code Generation :代碼生成,將 AST 轉換成低層次的IR指令。
-
Optimization :分析 IR 指令,將其中潛在會拖慢運行速度的指令干掉。
-
AsmPrinter :通過 IR(中間碼)生成特定 CPU 架構的匯編代碼。
-
Assemble :將匯編代碼轉化成二進制。
-
Linker :通常程序會引用其他的二進制文件(.a 或者 framework),但是這些鏈接在程序中沒有正確的地址,只是個占位符。Linker 的工作就是給這些占位符正確的地址。
-
更多信息可以參考:The Compiler
-
-
一般情況下,在真實的編譯器構架那種,會將上述過程分成前端和后端兩部分來處理:
- 在前后端之間傳遞的就是 IR(中間碼),而 Bitcode 就是一種特殊形式的中間碼。原本前后端的工作都是在本地 LLVM 中完成,雖然 Apple 沒有給出具體的 Bitcode 實現,但是通過他們的文檔可以猜測,是將一部分后端的工作移到了服務器進行。從 Xcode 上傳 IR 到服務器,服務器來真對不同的機型進行后續操作。從而達到真對不同機型生成對應指令集的二進制,而減小包體積的目的。
-
-
3、Bitcode 設置
-
實際上在 Xcode7 + 中,我們新建一個 iOS 程序時,Bitcode 選項默認是設置為 YES 的。我們可以在 Build Settings => Build Options => Enable Bitcode 選項中看到這個設置。
-
不過,我們現在需要考慮的是三個平台:iOS / Mac OS / watchOS。
- 對應 iOS,Bitcode 是可選的。
- 對於 watchOS,Bitcode 是必須的。
- Mac OS 不支持 Bitcode。
-
如果我們開啟了 Bitcode,在提交包時,下面這個界面也會有個 Bitcode 選項:
- 但是如果其中包含第三方庫,不支持 Bitcode 時候,需要將 Enable BitCode 設置成 NO。而且這個選項是只要有一個第三方庫不支持,就不能開的,否則連接錯誤。
-
確保打包的時候使用的是 fembed-bitcode, 而不是 fembed-bitcode-maker
-
You should be aware that a normal build with the -fembed-bitcode-marker option will produce minimal size embedded bitcode sections without any real content. This is done as a way of testing the bitcode-related aspects of your build without slowing down the build process. The actual bitcode content is included when you do an Archive build.
-
fembed-bitcode-maker:只是簡單的標記一下在 archive 出來的二進制中 Bitcdoe 所在的位置。
-
fembed-bitcode:真的會生成 Bitcode 指令,並且嵌入到二進制中,這個設置不止要在 app 中設置,同樣你也必須在編譯靜態鏈接庫的時候使用。而且需要主題的是該參數系統只默認在 archive 模式下會添加。
-
-
需要注意的是 Bitcode 只默認在 archive 下編譯。在 debug 和 release 下並不會。
- 如果您開發的是 app 那么走正常的打包 archive 流程就好了。如果你正在開發 .a 靜態庫或者 framework,請注意打包方式設置為 archive,或者在打包腳本中加入 -fembed-bitcode 參數。如果需要的話,需要在 Build Settings 中打開 DEPLOYMENT_POSTPROCESSING=YES,設置 Strip Style 為 debugging。
-
-
4、檢測是否打開 Bitcode
-
當打開 Bitcdoe 選項之后,我們可以使用 otool 工具來檢查二進制文件中是否包含 bitcode 段。
-
針對於靜態鏈接庫 .a 文件
otool -arch armv7 -l xxxx.a | grep __bitcode | wc -l
-
如果是當前庫支持 .a 文件則會輸出一個數字,如果不支持 Bitcode 則不會出現該數字。
-
上述命令只檢查了 armv7 架構,同時,也必須使用該指令檢查其他的指令集是否包含 Bitcode 如:arm64,armv7s 等等
-
-
檢查 app 或者 framework 中是否包含 Bitcode
-
由於 app 中二進制和 framework 中二進制文件與 .a 文件存在差異,因為需要檢查的是
__LLVM
段,當出現該段的時候,則表示支持 Bitcdoe,否則不支持。otool -l xxxx | grep __LLVM | wc -l
-
這里 otool 有個 bug,當你的 framework 使用過 lipo 命令,進行拆解和合並之后,需要指定指令集進行檢查才可以。
otool -arch armv7 -l xxxx | grep __LLVM | wc -l
-
BUT, 上述檢查過了之后,也不一定是真的支持 Bitcode,在實際的測試中,發現上述檢測命令通過之后,某個使用的第三方庫,依然報錯不支持 Bitcode。因而最終結果,還是需要以是否能夠連接成功為准。重要事情說三遍,上述網上流傳的檢測方法只做參考,最終還是要以實際效果為准。
-
-
最終結果檢查
-
如果您是一個 APP,可以直接進行 Archive 打包,如果是一個庫,則建議建一個 Demo 工程進行打包,記得要打開 Bitcode 設置。
-
CheckPoint1 連接是否報錯
-
如果有任何一個庫沒有打開 Bitcode 鏈接,將會出現類似下方的錯誤。只要鏈接過了,那么恭喜了,基本上是 OK 了。
-
-
CheckPoint2 檢查最終效果
-
使用開發模式導出 ipa
-
-
-
-
5、選擇出包的方式
-
這里建議使用第二種,生成針對具體機型的包
-
出現了,Compiling Bitcode,這個過程
-
-
在最后輸出的文件中,你能夠看到一個 App Thinning 的結果,里面有針對各個機型的 ipa 包。
-
在 App Thinning Size Report 中能夠明顯看到,由於使用了 Bitcode 等技術之后,所帶來的收益:
App Thinning Size Report for All Variants of Black Variant: Black-iPad (4th generation)-etc.ipa Supported devices: iPad (3rd generation) and iPad (4th generation) App + On Demand Resources size: 368 KB compressed, 737 KB uncompressed App size: 368 KB compressed, 737 KB uncompressed On Demand Resources size: Zero KB compressed, Zero KB uncompressed ....
-