當(dāng)前 iOS App 的編譯打包方式是把適配兼容多個設(shè)備的執(zhí)行文件及資源文件合并一個文件,上傳和下載的文件則包含了所有的這些文件,導(dǎo)致占用較多的存儲空間。
App Thinning 是一個關(guān)于節(jié)省 iOS 設(shè)備存儲空間的功能,它可以讓 App Store 和操作系統(tǒng)在安裝、更新及運行 iOS 或者 watchOS 的 App 等場景時,通過一系列的優(yōu)化,盡可能減少安裝包的大小,僅下載所需的資源,減少 App 的占用空間,從而節(jié)省設(shè)備的存儲空間。這個過程包括了三個過程:slicing、bitcode、on-demand resources。
一、slicing
App Slicing 在節(jié)省應(yīng)用所需資源中發(fā)揮著最重要的作用。
很多應(yīng)用需要在不同尺寸的設(shè)備上運行,針對這些不同的設(shè)備,它們內(nèi)含不同的獨立資源,而大部分是用戶設(shè)備不需要的。所以,開發(fā)者把 App 安裝包上傳到 AppStore 后,Apple 服務(wù)會自動將安裝包切割為不同的應(yīng)用變體(App variant),或者稱 App Store 會針對不同的設(shè)備制作不同的"簡化版 App",當(dāng)你下載 app 時,系統(tǒng)會根據(jù)設(shè)備型號下載安裝不同的"簡化版 app"。(iOS9.0.2 以上支持)
iOS app 為了后向兼容,現(xiàn)在都同時包含了 32 bit 和 64 bit 兩個 slice。另外在圖片資源方面,更是 2x、3x 的圖像一應(yīng)俱全。而用戶使用 app 時,因為設(shè)備是特定的,其實只需要其中的一套資源。但是現(xiàn)在在購買和下載的時候卻是把整個 app 包都下載了。Apple 在 iOS 9 中終于可以僅選擇需要的內(nèi)容 (Slicing) 下載了。對于開發(fā)者來說,并沒有太多要做的事情,只需要使用 asset catalog 來管理素材標(biāo)記 2x、3x 就可以了。
比如用戶使用的是 iPhone 5c,它運行的是 32 位 CPU 和 GPU,并不支持 Metal API。但如果用戶下載的是一款最新的通用游戲應(yīng)用,它的二進(jìn)制中含有 64 位代碼,iPad 和"@3x"iPhone 6 Plus 資源以及 Metal API 代碼,這些都是你的設(shè)備用不上的。它只需要 32 位代碼,"@2x"iPhone 尺寸資源以及 OpenGL 圖形代碼。

Note : Sliced apps are supported on devices running 9.0 and later;
Slicing 的主要的工作流程如下:
- 在 Xcode 中選擇好目標(biāo)設(shè)備并且使用 asset catalog 提供多分辨率的圖片資源;只有使用 asset catalog 才能正確使 Slicing 作用于資源文件。
- 在模擬器或者設(shè)備上編譯并運行 app;
- Xcode 會自動構(gòu)建針對你運行設(shè)備的"簡化版 app",同時也是為了減少編譯時間和進(jìn)行本地的測試;
- 打包 app(為了及時發(fā)現(xiàn)不同目標(biāo)設(shè)備的配置錯誤,可以在本地為目標(biāo)設(shè)備導(dǎo)出"簡化版 app",測試無誤后再打包)
- 上傳打包好的 app 到 iTunes connect。App Store 將會為上傳的 app 歸檔創(chuàng)建不同的"簡化版 app"。
- 在 iTunes Connect 中,發(fā)布一個預(yù)覽版給合格的測試者進(jìn)行測試;
- 測試者通過 TestFlight 下載預(yù)覽版。TestFlight 會自動根據(jù)測試者的設(shè)備下載合適的"簡化版 app"。
最終蘋果下載資源的效果如下:

二、Bitcode (iOS, watchOS)
開啟 Bitcode 編譯后,可以使得開發(fā)者上傳 App 時只需上傳 Intermediate Representation(中間件),而非最終的可執(zhí)行二進(jìn)制文件。在用戶下載 App 之前,AppStore 會自動編譯中間件,產(chǎn)生設(shè)備所需的執(zhí)行文件供用戶下載安裝。也就是當(dāng)我們提交程序到 App Store 上時, Xcode 會將程序編譯為一個中間表現(xiàn)形式( bitcode )。然后 App store 會再將這個 Bitcode 編譯為可執(zhí)行的 64 位或 32 位程序。蘋果會根據(jù)下載應(yīng)用的用戶的手機(jī)指令集類型生成只有該指令集的二進(jìn)制,進(jìn)行下發(fā)。從而達(dá)到精簡安裝包體積的目的。

Bitcode 是 LLVM 編譯器的中間代碼的一種編碼,LLVM 的前端可以理解為 C/C++/OC/Swift 等編程語言,LLVM 的后端可以理解為各個芯片平臺上的匯編指令或者可執(zhí)行機(jī)器指令數(shù)據(jù),那么,BitCode 就是位于這兩者之間的中間碼。LLVM 的編譯工作原理是前端負(fù)責(zé)把項目程序源代碼翻譯成 Bitcode 中間碼,然后再根據(jù)不同目標(biāo)機(jī)器芯片平臺轉(zhuǎn)換為相應(yīng)的匯編指令以及翻譯為機(jī)器碼。這樣設(shè)計就可以讓 LLVM 成為了一個編譯器架構(gòu),可以輕而易舉的在 LLVM 架構(gòu)之上發(fā)明新的語言(前端),以及在 LLVM 架構(gòu)下面支持新的 CPU(后端)指令輸出。
雖然 Bitcode 僅僅只是一個中間碼不能在任何平臺上運行,但是它可以轉(zhuǎn)化為任何被支持的 CPU 架構(gòu),包括現(xiàn)在還沒被發(fā)明的 CPU 架構(gòu),也就是說現(xiàn)在打開 Bitcode 功能提交一個 App 到應(yīng)用商店,對于 Apple 未來進(jìn)行硬件升級的措施,此機(jī)制可以保證在開發(fā)者不重新發(fā)布版本的情況下而兼容新的設(shè)備。比如以后如果蘋果新出了一款手機(jī)并 CPU 架構(gòu)也是全新設(shè)計的,在蘋果后臺服務(wù)器一樣可以從這個 App 的 Bitcode 開始編譯轉(zhuǎn)化為新 CPU 上的可執(zhí)行程序,可供新手機(jī)用戶下載運行這個 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 中被關(guān)閉了,因為一個 iCloud 的 bug)。實際上 Bitcode,做的事情是指令集優(yōu)化。根據(jù)你設(shè)備的狀態(tài)去做編譯優(yōu)化,進(jìn)而提升性能。所以 Bitcode 對包的大小優(yōu)化起不到什么本質(zhì)上的作用。
Bitcode 注意點
- Xcode 7 默認(rèn)開啟 Bitcode,如果應(yīng)用開啟 Bitcode,那么其集成的其他第三方庫也需要是 Bitcode 編譯的包才能真正進(jìn)行 Bitcode 編譯
- 開啟 Bitcode 編譯后,編譯產(chǎn)生的 .app 體積會變大(中間代碼,不是用戶下載的包),且 .dSYM 文件不能用來崩潰日志的符號化(用戶下載的包是 Apple 服務(wù)重新編譯產(chǎn)生的,有產(chǎn)生新的符號文件)
- 通過 Archive 方式上傳 AppStore 的包,可以在 Xcode 的 Organizer 工具中下載對應(yīng)安裝包的新的符號文件
三、On-Demand Resources (iOS)
ODR(on-demand resources 隨需應(yīng)變資源)是 iOS 減少應(yīng)用資源消耗的另外一種方法。比如多級游戲,用戶需要的通常都是他們當(dāng)前的級數(shù)以及下一級。ODR 意味著用戶可以下載他們需要的幾級游戲。隨著你的級數(shù)不斷增加,應(yīng)用再下載其他級數(shù),并將用戶成功過關(guān)的級數(shù)刪掉。

當(dāng)用戶點擊應(yīng)用內(nèi)容的時候,就會動態(tài)從 App Store 上進(jìn)行下載,也就是說用戶只會在需要的時候占用存儲空間。這項功能有趣之處還在于當(dāng)將這些內(nèi)容在后臺進(jìn)行下載之后,當(dāng)存儲空間緊張的時候會自動進(jìn)行刪除。

On-Demand Resources 可以是除了可執(zhí)行代碼外的任意類型。

在開發(fā)過程中,你可以通過分配一個或多個 tag 來識別 On-Demand Resources。你可以使用 tag 的別名來確定什么時候?qū)⑺虞d到你的 App 中。
好處:
- Smaller app size. app 體積更小。
- Lazy loading of app resources. 懶加載應(yīng)用資源。
- Remote storage of rarely used resources. 遠(yuǎn)端存儲較少使用的資源。
- Remote storage of in-app purchase resources. 遠(yuǎn)端存儲內(nèi)購資源。
下圖展示了一個在 App 中保持最小資源占用的例子。


可以給資源設(shè)置優(yōu)先級,比如當(dāng) App 從 AppStore 安裝后就立即加載。
A _tag_ is a string identifier you create. Apps request tags, not individual resources.
3.1 On-Demand Resources 的生命周期
-
App 向操作系統(tǒng)請求資源。操作系統(tǒng)將請求發(fā)送給包含所有所需資源的 asset packs。
asset packs 檢查請求的資源本地是否存在。如果存在,則直接提供 App 使用。
-
如果請求的資源本地不存在,則它們被保存在 App Store。
操作系統(tǒng)開始下載本地不存在的資源
-
遠(yuǎn)程資源下載完畢
-
當(dāng)資源下載成功或監(jiān)測到資源包已經(jīng)被下載,資源包內(nèi)存計數(shù)將會被 +1,并通知 App 此資源可用。
-
當(dāng)請求的資源可用,App 使用資源標(biāo)簽對應(yīng)的資源。
操作系統(tǒng)在本地釋放資源標(biāo)簽
操作系統(tǒng)在本地清除資源緩存。當(dāng)一個緩存資源不與任何請求相關(guān)聯(lián)時,操作系統(tǒng)會在一定時間后將它釋放掉。
完整的生命周期如下圖所示:
[站外圖片上傳中...(image-2f1cb9-1575442059004)]
四、實際處理方法
-
iOS9 以后 Xcode 默認(rèn)開啟 On-Demand Resources 功能,可以在下圖所示位置進(jìn)行設(shè)置。
-
在 App 中創(chuàng)建 Tags
[站外圖片上傳中...(image-75fd52-1575442059004)]
-
給文件設(shè)置 tag
-
給圖片設(shè)置 tag
-
給 tag 設(shè)置加載的優(yōu)先級
4.1 加載優(yōu)先級
- Initial install tags.資源和 App 同時下載。在 App Store 中,App 的大小計算已經(jīng)包含了這部分資源。當(dāng)沒有NSBundleResourceRequest 對象訪問它們時,它們將會從設(shè)備上清除。
- Prefetch tag order. 在 App 安裝后開始下載,按照預(yù)加載列表中的順序依次下載。
- Dowloaded only on demand. 只有在 App 中發(fā)出請求時才會下載。
4.2 資源大小限制

五、學(xué)習(xí)文章
What is app thinning? (iOS, tvOS, watchOS)
On-Demand Resources Essentials
App Thinning
App Thinning









