在做安裝包大小優化前,我們應該首先搞清楚,用戶在 app store 上看到的包大小,究竟是什么?
如果我們衡量安裝包大小的口徑,和用戶看到的大小不一致,那么做優化時的優先級和ROI衡量就可能跑偏,甚至出現優化效果為負的悲慘結局。
首先拋出結論:用戶在 app store 上看到的包大小,是:
- .app 文件
- 的二進制部分被加殼后
- 再經過 app slicing
的大小。
如何查看
在某個版本的 app 上線之前,開發者應該如何知曉它在用戶眼中的大小呢?
蘋果的 itunes connect 后臺為開發者提供了查看安裝包大小的功能。
在 itunes connect 后臺,開發者可以看到當前版本針對不同機型的大小。
這里的大小分為兩個口徑:
Download Size 和 Install Size。
根據網頁上的說明:
Install Size 是這個 app 安裝后,會占用的磁盤大小;
Download Size 是 app 經過壓縮后的大小。
根據經驗,用戶在 app store 上看到的大小,就是 itunes connect 后臺中顯示的 Install Size。
而令開發者在意的,“超過 150 MB 的 app 必須連接至無線局域網才能下載”的規則中的 150 MB,指的其實是 Download Size。
幾個口徑
Download Size 和 Install Size 是如何計算出來的,我們等下再說。
拋開 itunes connect 和 app store,平時我們在開發打包一個 app 時,經常會接觸到這樣幾個安裝包概念:
- ipa
- app
ipa 可能是我們最熟悉的“安裝包”的格式。通過 Xcode 的 archive 方式,最終打出的安裝包的格式就是 ipa。
實際上,ipa 就是一個 zip 壓縮包。我們可以用 unzip 的方式來解壓一個 ipa 文件。
這樣,我們會得到一個后綴名為.app的文件。
甚至可以想進入一個文件夾一樣進入到這個.app中,窺探二進制、各個資源分別占據了多少大小。
這里我們知道了,.app 文件和 .ipa 文件的關系:
ipa 文件是 app 文件 zip 壓縮后的產物。
用戶看到的大小
那么用戶看到的 Install Size,究竟是 .app 文件的大小,還是 .ipa 文件的大小呢?
答案:都不是。但是 .app 文件的大小與 Install Size 的口徑更為接近。
加殼
蘋果在 iOS 9 推出了能減小安裝包的 app thining 功能。我們先不考慮這個功能,看看 iOS 9 以前 Install Size 與 .app 文件的關系。
要獲取 app store 上的安裝包,其實沒有正規的做法。有一個比較trick的技巧可以獲得:
http://www.lxweimin.com/p/ce018473fad0
通過這個方式,我們可以下載到一個 .ipa 文件。
這個 ipa 文件經解壓后得到的 app 文件,其大小與 iOS 9 以下設備在 app store 上看到的大小是吻合的。也與 itunes connect 中,開發者看到的 Install Size 是吻合的。
但是,它與開發者提交到 itunes connect 前的 app 文件大小,是有一定差距的。
比如,我們提交到 app store 上的 ipa 解壓后的大小為 297.1MB
從 app store 上下載的 ipa 解壓后大小為 301.9MB
其中這 3.8 MB 的大小差異來自哪里呢?
來自加殼。
我們對比了這兩個 app 包中的各個文件大小,發現各資源文件的大小完全一致,只有二進制文件的大小發生了改變。
使用 otool 命令
otool -l 可執行文件路徑 | grep crypt
我們可以驗證,app store 中下載的包經過了加殼,而提交 itunes connect 前的包沒有。
使用
otool -l
命令可以輸出 Mach-O 文件加載的 load command。經對比發現,雖然加殼改變了TEXT段的內容,卻沒有改變TEXT段的大小。這 3.8 MB 的大小差異主要來自 __LINKEDIT 和 LC_CODE_SIGNATURE 這兩個段,這兩個段都與動態鏈接器有關。
因此,我們可以得到結論:iOS 9 之前,用戶看到的 Install Size 的大小,是 .app 文件經過加殼的大小,粗略的可以認為就是 .app 文件的大小。
app slicing
上述的分析針對于 iOS 9 以下設備看到的情況。
如果考慮蘋果在 iOS 9 上推出的 app thining 功能,安裝包大小會有什么影響呢?
我們先了解一下,如果沒有 app thining,一個安裝包中會包含哪些內容。
對二進制來說,由于一個安裝包需要同時支持 iPhone 5 等 32 位設備和 iPhone 5s 以上的 64 位設備,所以二進制中需要包含 armv7 和 arm64 兩個架構的 Mach-O 文件。
對于放在 asset catalog 中的資源來說,一般來說,開發者為了更好的適配 iPhone 6 等 2x 屏幕的設備和 iPhone 6 plus 等 3x 屏幕的設備,每一個圖片資源會引入 2x 和 3x 兩個版本。
對于其他圖片/資源來說,它們也會被收入到安裝包中。
這里,我們可以明顯發現,如果一個設備下載安裝了安裝包中的全部內容,有兩個明顯浪費的地方:
- 有一份它不需要的 Mach-O
- 有一份它不需要的資源圖
蘋果在 iOS 9 推出的 app slicing,就幫助開發者將這兩個浪費的地方干掉了。
對于資源圖,官方文檔中有說明:
A thinned .ipa is a compressed app bundle that contains only the resources needed to run the app on a specific device.
https://developer.apple.com/library/archive/qa/qa1795/_index.html
對于二進制,我們沒有在官方文檔中找到說明。但 WWDC App Thinning in Xcode 一篇中這樣介紹了 app slicing 功能:
這張圖中體現了,無論是二進制還是 asset catalog 中的資源,app slicing 都只保留了當前設備所需的部分。
而在導出 ipa 時,在配置 plist 文件中加入 <thin-for-all-variants> 選項,則可以導出針對不同機型的 ipa 文件。經實驗,這些 ipa 文件解壓后得到的 app 文件大小,與 itunes connect 后臺各個設備對應的 Install Size 是吻合的。
優化建議
正如文章開頭的結論:
用戶在 app store 上看到的包大小,是:
- .app 文件
- 的二進制部分被加殼后
- 再經過 app slicing
的大小。
得知了用戶看到的包大小究竟是什么含義,我們可以避開一些包大小優化的坑。
比如,由于 app slicing 的存在,圖標應該盡可能用 asset catalog 管理起來。所以試圖用 webp 等格式來替代 asset catalog,可能是負向收益。
(這個思路經過實驗,結論是,在我們的業務場景下,如果用 webp 替代 asset catalog,對于 2x 設備的 Download Size 是負向收益,因為 2x 設備不得不使用 3x 的圖片,并且 webp 圖的壓縮率比 asset catalog 低很多)
再比如,不要幻想拋棄 armv7 架構的設備,可以減小包大小。蘋果已經幫開發者做過這一步了。