前言
應用的包體積大小會影響用戶的點擊下載率、安裝成功率和卸載率,是衡量APP性能的一項重要指標。為了更好的用戶體驗,減少用戶下載等待時長、減少手機存儲空間占用,對包體積大小的優化也是尤為重要的。
安裝包ipa的內容
iOS打包出來的ipa,本質上是一個壓縮包,可以將.ipa的后綴改為.zip,然后進行解壓縮后會得到一個Payload文件夾,里面又一個xxx.app的文件,右鍵顯示包內容可以看到具體內容:
- _CodeSignature: ipa包簽名文件
- .lproj: 語言文件
- Frameworks: 第三庫、SwiftSupport庫
- Plugins: App創建的擴展,比如Widget、Push、分享
- Assets.car: 由Assets.xcassets生成的資源文件,里面包含各種分辨率的圖片
- embedded.mobileprovision: 證書配置文件
- Info.plist: 項目配置
- exec格式的xxx: Mach-O格式的可執行文件
- 其它資源文件
.mp3格式的文件
.html的文件
.json的文件
.png或者.jpg的文件
Mach-o文件的具體內容:
Header.png
- Header,存放了二進制文件的基本信息,包括文件是32位還是64位、運行該文件對應的處理器架構是什么、文件類型(比如可執行文件)、Load Commands的個數和大小等等。
- Load Commands,是一個struct結構,用來告訴內核和dyld,如何將APP運行所需資源加載到內存。比如main的加載地址,動態鏈接器dyld的文件路徑,相關依賴庫的文件路徑,以及Data中的Segment如何加載到內存。
- Data數據區,存放著代碼、字符常量、類、方法等數據,由多個segment組成。而segment是由多個section組成的。
segment包括如下5種:
- _PAGEZERO: 占用APP最開始的一段內存空間,用來處理空指針;
- _TEXT : 只讀,用于存放代碼、字符串常量,const修飾常量等;
- _DATA : 可讀寫,用于存儲程序中所定義的數據;
- __OBJC: Objective-C runtime 段;
- _LINKEDIT: 包含需要被動態鏈接器使用的符號和其他表,包括符號表、字符串表等。
包體積大小優化
通過ipa安裝包的具體內容,可以分析出應用包體積大小的具體優化方案。
1 資源瘦身
1.1 移除無用資源
- 可以使用LSUnusedResources這類工具,查找應用沒有使用的圖片資源。
- 內容相同,但是尺寸不同的圖片;同樣的圖片,在不同業務模塊使用了不同的命名(可以通過fdupes工具掃描出重復的文件)。這兩類圖片可以考慮合并處理。
1.2 資源壓縮
- 使用 RGB with palette 壓縮圖片。
通過ImageOptim、tinypng等工具進行無損壓縮,是通過變換圖片的編碼壓縮算法減少文件的大小,圖片解碼后的Bitmap數據不變,構建Asset Catalog的工具actool以Bitmap為數據源,所以無損壓縮無法優化Assets.car的大小。
ImageOptim可以改變圖片的編碼方式為RGB with palette,需要在ImageOptim的設置頁面勾選有損壓縮,指定相應的壓縮質量即可。 - 使用webp格式的圖片,有損壓縮模式下圖片體積只有 jpeg 格式的 1/3,無損壓縮也能減小 1/4。
- Assets.car合并,可以在Build Phases中加入腳本,將多個庫中Asset Catalog合并到一個Asset Catalog中。
Assets.car 文件本質上是BOM文件,actool構建Assets.car文件時會自帶一些優化操作。所以,將若干個 Assets.car 合并,可以減少重復的 BOM Block,最大化的享受actool優化效果。 - 文本文件壓縮,壓縮部分json、html等文本文件,在應用啟動后解壓放到沙盒中,也可以優化一部分的包體積大小。
2 Mach-o文件優化
2.1 Optimization Level使用-Oz編譯參數
Xcode 11新增了Oz編譯優化選項,核心原理是對重復的連續機器指令外聯成函數進行復用,和“內聯函數”的原理正好相反。因此,開啟Oz可以減小二進制文件的大小,但也會帶來執行效率的額外消耗,需要慎用(記一起 clang 開啟 -Oz 選項引發的血案)。
2.2 Link-Time Optimization鏈接時優化
Link-Time Optimization鏈接時優化是Xcode自帶的一個編譯/鏈接參數,對包大小和運行效率都有正向影響。
Apple Clang Code Generation.png
2.3 屬性動態化
一個屬性可以分為三個部分:
- 成員變量部分: 成員變量本質是一個大小 32B 的結構體,結構體中三個指針(Offset、Name、Type)指向的內容的大小分別為8B、10B、10B,其中Name、Type指針指向的內容的大小和成員變量的類型、名字長度相關??偞笮〈蠹s60B。
- 自動生成的 set/get 方法部分: set/get 方法本質是一個大小 24B的結構體,結構體包含三個指針Name、Type、Implementation,指向的內容大小大概為10B、10B、20B。一個方法大小大概是 64B,set、get 兩個方法就是 128B。
- property 部分: property 的本質仍然是個結構體,大小是 16B,結構體中兩個指針指向內容的大小分別大概是10B、10B,和屬性的名字和類型相關??偞笮〈蟾?36B。
因此,一個屬性占用的包大小大約為 224B。如果指定屬性@dynamic,就不生成成員變量、get/set 方法,則一個屬性可以由 224B 減少到 36B。所以,應用內Model的屬性可以考慮指定@dynamic。
參考文章
詳解 Mach-O 文件結構
iOS進階 - Mach-O文件解析
Xcode編譯相關
記一起 clang 開啟 -Oz 選項引發的血案
今日頭條 iOS 安裝包大小優化—— 新階段、新實踐
Alibaba.com瘦包40MB——業界最全的iOS包大小技術總結
深入探索 iOS 包體積優化
正經分析iOS包大小優化
ipa包大小優化