當前 iOS App 的編譯打包方式是把適配兼容多個設備的執行文件及資源文件合並一個文件,上傳和下載的文件則包含了所有的這些文件,導致占用較多的存儲空間。
App Thinning 是一個關於節省 iOS 設備存儲空間
的功能,它可以讓 App Store 和操作系統在安裝、更新及運行 iOS 或者 watchOS 的 App 等場景時,通過一系列的優化,盡可能減少安裝包的大小,僅下載所需的資源,減少 App 的占用空間,從而節省設備的存儲空間。這個過程包括了三個過程:slicing、bitcode、on-demand resources。
一、slicing
App Slicing 在節省應用所需資源中發揮着最重要的作用。
很多應用需要在不同尺寸的設備上運行,針對這些不同的設備,它們內含不同的獨立資源,而大部分是用戶設備不需要的。所以,開發者把 App 安裝包上傳到 AppStore 后,Apple 服務會自動將安裝包切割為不同的應用變體(App variant),或者稱 App Store 會針對不同的設備制作不同的"簡化版 App",當你下載 app 時,系統會根據設備型號下載安裝不同的"簡化版 app"。(iOS9.0.2 以上支持)
iOS app 為了后向兼容,現在都同時包含了 32 bit 和 64 bit 兩個 slice。另外在圖片資源方面,更是 2x、3x 的圖像一應俱全。而用戶使用 app 時,因為設備是特定的,其實只需要其中的一套資源。但是現在在購買和下載的時候卻是把整個 app 包都下載了。Apple 在 iOS 9 中終於可以僅選擇需要的內容 (Slicing) 下載了。對於開發者來說,並沒有太多要做的事情,只需要使用 asset catalog 來管理素材標記 2x 3x 就可以了。
比如用戶使用的是 iPhone 5c,它運行的是 32 位 CPU 和 GPU,並不支持 Metal API。但如果用戶下載的是一款最新的通用游戲應用,它的二進制中含有 64 位代碼,iPad 和"@3x"iPhone 6 Plus 資源以及 Metal API 代碼,這些都是你的設備用不上的。它只需要 32 位代碼,"@2x"iPhone 尺寸資源以及 OpenGL 圖形代碼。
Note : Sliced apps are supported on devices running 9.0 and later;
Slicing 的主要的工作流程如下:
- 在 Xcode 中選擇好目標設備並且使用 asset catalog 提供多分辨率的圖片資源;只有使用 asset catalog 才能正確使 Slicing 作用於資源文件。
- 在模擬器或者設備上編譯並運行 app;
- Xcode 會自動構建針對你運行設備的"簡化版 app",同時也是為了減少編譯時間和進行本地的測試;
- 打包 app(為了及時發現不同目標設備的配置錯誤,可以在本地為目標設備導出"簡化版 app",測試無誤后再打包)
- 上傳打包好的 app 到 iTunes connect。App Store 將會為上傳的 app 歸檔創建不同的"簡化版 app"。
- 在 iTunes Connect 中,發布一個預覽版給合格的測試者進行測試;
- 測試者通過 TestFlight 下載預覽版。TestFlight 會自動根據測試者的設備下載合適的"簡化版 app"。
最終蘋果下載資源的效果如下:
二、Bitcode (iOS, watchOS)
開啟 Bitcode 編譯后,可以使得開發者上傳 App 時只需上傳 Intermediate Representation(中間件),而非最終的可執行二進制文件。在用戶下載 App 之前,AppStore 會自動編譯中間件,產生設備所需的執行文件供用戶下載安裝。也就是當我們提交程序到 App Store 上時, Xcode 會將程序編譯為一個中間表現形式( bitcode )。然后 App store 會再將這個 Bitcode 編譯為可執行的 64 位或 32 位程序。蘋果會根據下載應用的用戶的手機指令集類型生成只有該指令集的二進制,進行下發。從而達到精簡安裝包體積的目的。
Bitcode 是 LLVM 編譯器的中間代碼的一種編碼,LLVM 的前端可以理解為 C/C++/OC/Swift 等編程語言,LLVM 的后端可以理解為各個芯片平台上的匯編指令或者可執行機器指令數據,那么,BitCode 就是位於這兩者之間的中間碼。LLVM 的編譯工作原理是前端負責把項目程序源代碼翻譯成 Bitcode 中間碼,然后再根據不同目標機器芯片平台轉換為相應的匯編指令以及翻譯為機器碼。這樣設計就可以讓 LLVM 成為了一個編譯器架構,可以輕而易舉的在 LLVM 架構之上發明新的語言(前端),以及在 LLVM 架構下面支持新的 CPU(后端)指令輸出。
雖然 Bitcode 僅僅只是一個中間碼不能在任何平台上運行,但是它可以轉化為任何被支持的 CPU 架構,包括現在還沒被發明的 CPU 架構,也就是說現在打開 Bitcode 功能提交一個 App 到應用商店,對於 Apple 未來進行硬件升級的措施,此機制可以保證在開發者不重新發布版本的情況下而兼容新的設備。比如以后如果蘋果新出了一款手機並 CPU 架構也是全新設計的,在蘋果后台服務器一樣可以從這個 App 的 Bitcode 開始編譯轉化為新 CPU 上的可執行程序,可供新手機用戶下載運行這個 App。
在文檔里可看到
In fact, app slicing handles the majority of the app thinning process. ‘App Slicing’ feature finally switched on in iOS 9.0.2
說明 slicing 才是主要處理 app thinning 的,而且該功能需要在 iOS9.0.2 以上才支持(iOS9.0 中被關閉了,因為一個 iCloud 的 bug)。實際上 Bitcode,做的事情是指令集優化。根據你設備的狀態去做編譯優化,進而提升性能。所以 Bitcode 對包的大小優化起不到什么本質上的作用。
Bitcode 注意點
- Xcode 7 默認開啟 Bitcode,如果應用開啟 Bitcode,那么其集成的其他第三方庫也需要是 Bitcode 編譯的包才能真正進行 Bitcode 編譯
- 開啟 Bitcode 編譯后,編譯產生的 .app 體積會變大(中間代碼,不是用戶下載的包),且 .dSYM 文件不能用來崩潰日志的符號化(用戶下載的包是 Apple 服務重新編譯產生的,有產生新的符號文件)
- 通過 Archive 方式上傳 AppStore 的包,可以在 Xcode 的 Organizer 工具中下載對應安裝包的新的符號文件
三、On-Demand Resources (iOS)
ODR(on-demand resources 隨需應變資源)是 iOS 減少應用資源消耗的另外一種方法。比如多級游戲,用戶需要的通常都是他們當前的級數以及下一級。ODR 意味着用戶可以下載他們需要的幾級游戲。隨着你的級數不斷增加,應用再下載其他級數,並將用戶成功過關的級數刪掉。
當用戶點擊應用內容的時候,就會動態從 App Store 上進行下載,也就是說用戶只會在需要的時候占用存儲空間。這項功能有趣之處還在於當將這些內容在后台進行下載之后,當存儲空間緊張的時候會自動進行刪除。
On-Demand Resources 可以是除了可執行代碼外的任意類型。
在開發過程中,你可以通過分配一個或多個 tag 來識別 On-Demand Resources。你可以使用 tag 的別名來確定什么時候將它加載到你的 App 中。
好處:
- Smaller app size. app 體積更小。
- Lazy loading of app resources. 懶加載應用資源。
- Remote storage of rarely used resources. 遠端存儲較少使用的資源。
- Remote storage of in-app purchase resources. 遠端存儲內購資源。
下圖展示了一個在 App 中保持最小資源占用的例子。
可以給資源設置優先級,比如當 App 從 AppStore 安裝后就立即加載。
A _tag_ is a string identifier you create. Apps request tags, not individual resources.
3.1 On-Demand Resources 的生命周期
-
App 向操作系統請求資源。操作系統將請求發送給包含所有所需資源的 asset packs。
 -
asset packs 檢查請求的資源本地是否存在。如果存在,則直接提供 App 使用。
-
如果請求的資源本地不存在,則它們被保存在 App Store。
 -
操作系統開始下載本地不存在的資源
-
遠程資源下載完畢
 -
當資源下載成功或監測到資源包已經被下載,資源包內存計數將會被 +1,並通知 App 此資源可用。
 -
當請求的資源可用,App 使用資源標簽對應的資源。
 -
操作系統在本地釋放資源標簽
-
操作系統在本地清除資源緩存。當一個緩存資源不與任何請求相關聯時,操作系統會在一定時間后將它釋放掉。
完整的生命周期如下圖所示:
四、實際處理方法
-
iOS9 以后 Xcode 默認開啟 On-Demand Resources 功能,可以在下圖所示位置進行設置。
 -
在 App 中創建 Tags
 -
給文件設置 tag
 -
給圖片設置 tag
 -
給 tag 設置加載的優先級

4.1 加載優先級
- Initial install tags.資源和 App 同時下載。在 App Store 中,App 的大小計算已經包含了這部分資源。當沒有NSBundleResourceRequest 對象訪問它們時,它們將會從設備上清除。
- Prefetch tag order. 在 App 安裝后開始下載,按照預加載列表中的順序依次下載。
- Dowloaded only on demand. 只有在 App 中發出請求時才會下載。
4.2 資源大小限制
五、學習文章
What is app thinning? (iOS, tvOS, watchOS)
On-Demand Resources Essentials
App Thinning
App Thinning