文章最後有我的 12 條小總結。
原文始發地址:我的 GitHub
寫在前面
- 最近公司需求不多,正好研究一下 App 瘦身的辦法,寫了點小總結。
- 如果你不知道下面幾個問題,不妨可以看看文章。
- 使用 .xcassets 有什麼好處?
- @1x 、@2x 和 @3x 會一起內建到安裝包中嗎?
- PDF 和 @1x 、@2x 和 @3x 有什麼區別?
- 如果我有一個 10 x 10 的控制元件和一個 50 x 50 的控制元件,美工需要製作幾張 PDF?
- Iconfont 是什麼?PDF 和 Iconfont 有什麼區別?
- 啟動圖的正確開啟方式?
- 使用 Swift 或者 混編會增大多少的包體積?
- Install Smallest or Coding Fastest ?
分析
- 在瘦身之前,首先需要分析一下,我們可以從哪幾個方面入手。(以 Yep 為例)
Yep是一款很優秀的 Swift 開源軟體。
目錄劃分
- App 的瘦身主要是針對於安裝包,而在 iOS 中安裝包就是一個以 .ipa 結尾的壓縮包。我們可以通過 iTunes 下載獲取這個 .ipa 來分析。
稍微整理一下,大致可以分為以下幾類。
資源層面:
- Assets.car:專案中所有 .xcassets 的壓縮包
- image: 圖片資原始檔
- Video && Audio :音訊 或者 視訊。
程式碼層面:
- 國際化:國際化適配的 String ===> 89K
- Xib && Storyboard:Xib 和 Storyboard 編譯後的檔案。
- Yep :專案可執行檔案。
- Frameworks:Embedded Frameworks,專案中使用的動態庫
其他:
- other:配置檔案
PlugIns:YepShare,一個共享的外掛。
- 雖然 Yep 不能代表所有的 App,但是在對於 Yep 的 ipa 分析之後,大致可以總結出,對一個 App 的安裝包瘦身,可以從資源層面和程式碼層面兩個層面入手。
資源層面
- 在講資源層面之前,希望我們能達到一個共識,那就是所謂的資原始檔指的是 圖片、視訊、音訊。
- Remote : 將資原始檔放在伺服器上,當使用者下載完 App 後根據需要再下載。
- Local : 將資原始檔整合到安裝包中的。
Remote
- 對於 Remote 的方式,如果做好策略(比如快取),那麼理論上,我們可以把 非必須的資原始檔 都放到伺服器上,這樣對資源壓縮率達到了 100%。也就是說安裝包 沒有任何非必須資原始檔 。
- 必須資原始檔:例如應用圖示、啟動圖的這種配置圖片。
- 蘋果的 On-Demand Resources(翻譯) 也是通過這種按需載入資源的思路給我們提供了一種階段性載入資源的途徑,具體的不展開描述,你可以點前面的連結進行檢視。但是雖然以關卡、tag這種方式來按需載入資源,但是蘋果的伺服器對於中國使用者來說實在是慢的不行,所以暫時不建議採取這種方式。你們可以在自己伺服器上實現這種策略方式來載入圖片。
Local
- 當然全部將非必須資原始檔放到伺服器上明顯是不現實的,對於一些必用資原始檔,還是需要將資原始檔 整合到安裝包中的。
- 必用資原始檔:安裝了 App 肯定會用到。
Local 整合方式
- Create group 和 Create folder references
- 這兩種其實就是直接把資原始檔 拖 進去,在 Xcode 打包之後,所有圖片都在可執行檔案的相同目錄下面。這也是很多老的 App 或者目前部分 App 的使用方式。
- .xcassets
- 這是蘋果在 Xcode 5 出來之後,推薦我們使用的圖片管理方式,提供了圖片渲染、拉伸模式模式、機型適配等功能。在 Xcode 打包之後所有的.xcassets 檔案都會放入一個Assets.car檔案中。
Local 開發使用方式列舉分析。
- 一般 App 的圖片內建方式
- 採用拖的方式,圖片包含@1x、@2x 和 @3x。
- 採用拖的方式,圖片只包含 @2x 和 @3x。
- 採用拖的方式,圖片只包含 @2x 或 @3x。
- 採用.xcassets的方式,圖片包含@1x、@2x 和 @3x。
- 採用.xcassets的方式,圖片只包含@2x 和 @3x。
- 採用.xcassets的方式,圖片只 包含@2x 或 @3x。
- 採用.xcassets的方式,圖片使用 PDF。
(可能還有其他方式,希望你能告訴我。)
拖的方式
- 首先@1x、@2x 和 @3x主要是為了適配不同 ppi 的機子而做了一種策略。@1x 主要是為了適配 iPhone 4 之前的 非 Retina 螢幕,@2x 主要是為了適配 非 plus 的 iPhone 裝置, @3x 是為了適配一個點的 3 * 3 個畫素的手機。
- 因為 iPhone 4 之前的機子基本沒有什麼 App 會去適配,所以一般來說都會刪除,所以就有了 『採用拖的方式,圖片只包含@2x 和 @3x』 的方式。但是假如你只提供一張圖片,例如你只提供了一張 @3x 的圖片,iOS 系統在 iPhone 7 上無法找到 @2x 的圖片,會去查詢 @3x 或者 @1x 等,再根據實際解析度進行拉伸,最後把畫素鋪到螢幕上。所以在能夠接受查詢和拉伸造成的效能消耗的前提下,我們可以只用一張通用的圖片,所以就有了 『採用拖的方式,圖片包含@2x 或 @3x』 的方式。
- 以一個 14M 的圖片資源(包含@1x 、@2x、@3x)來說,如果所有的圖片去除掉 @1x 能減少 1M 左右,去除掉@2x能去掉 4M 左右。因此採用『採用拖的方式,圖片包含@2x 或 @3x』的方式雖然損失了一點效能,單大概圖片資源大概減少了35%左右,。
採用.xcassets的方式
- 我們都知道了,採用『採用拖的方式,圖片包含@2x 或 @3x』的方式大概圖片資源大概減少了35%左右,但是稍微損失了一點效能。有什麼方式可以減少掉這點效能消耗呢?
“很幸運” ,蘋果在 iOS 9 終於意識到了這個問題,然後提供了一個叫做 App Slicing(如下圖所示)的東西。App Slicing大致就是App Store會根據不同的裝置準備不同的安裝包(App Variant),每個安裝包(App Variant)都只有相應尺寸的圖片,比如 iPhone 6 去下載時,只會下載到 @2x 的圖片的安裝包(App Variant)。但能實現這個功能的前提是圖片需要放置在.xcassets去管理。
所以,目前許多 App 採用 『.xcassets的方式,圖片只 包含@2x 或 @3x』 其實是沒意義的,特別是在你不適配 iOS 8 的時候,你這麼做是強行降低了 App 的效能。當然你要覺得為了 8% 的非 iOS 9 使用者 減少 App 安裝包大小 而去降低另外 92% 的使用者的 App 執行效能 沒什麼問題,那麼你可以採取上面這種方式。
關於 PDF
- 我最早是在這一篇部落格中看到的,當然 Yep 也是這種方式。大致是刪掉 @2x 和 @3x 的圖片,然後替換成 向量圖 PDF,最後放入.xcassets中去。
- 而 Xcode 在打包的過程中,根據你的向量PDF圖的大小,生成@1x、@2x和@3x的圖。例如你的PDF圖是4545px,那麼Xcode會在編譯時生成下面3個png:4545px 、9090px、135135px,最後再放入Assets.car中。所以採用@1x、@2x 和 @3x 和 PDF 兩種方式本質上是一樣的。
- 在這裡有很多人會有一個誤解,例如在 App 中有一個 10 10 pt 和 一個 50 50 pt 的 imageView 都用了一個相同的圖示。很多人會以為做一個就夠了,因為 pdf 是向量圖。但是其實是需要一個 10 10 px 和 50 50 px 的兩張 pdf 才可以,因為只用一張的話,另外一張用的其實就是 10 * 10px 的 PDF 的產物。
關於壓縮問題。
- 我是用tinypng 來壓縮的,應該是以最小的佔用量達到了最適合的效果。但是其實.xcassets 也會為你做一部分的壓縮。如下圖所示:
- .xcassets 的壓縮應該還對圖片進行了處理這也就是為什麼 840KB 壓縮了 81.5%,Assets.car卻沒有減少那麼多。
- 同時也有人在試驗中發現,用一些壓縮工具似乎沒有很麼實際效果,這也有可能是因為 Xcode 在打包的時候做了一定的處理。
- 我是用tinypng 來壓縮的,應該是以最小的佔用量達到了最適合的效果。但是其實.xcassets 也會為你做一部分的壓縮。如下圖所示:
啟動圖
- 啟動圖在一個專案資源中佔比其實蠻大的,之前見過一個專案 6 張啟動圖大概有5M 左右,最大的是2M。
- iPad 2 and iPad mini (@1x): 768 x 1024
- iPad and iPad mini (retina @2x): 1536 x 2048
- iPhone 4s (retina @2x) 640 x 960
- iPhone 5 (@2x): 640 x 1136
- iPhone 6 (@2x): 750 x 1334
- iPhone 6 Plus (@3x): 1242 x 2208
- 但是自從LaunchScreen.storyboard出來一後完全沒必要做這麼多張了。只需要將啟動圖設定為LaunchScreen.storyboard 然後在LaunchScreen.storyboard上設定一張 imageView 。最後再弄一張啟動圖的 pdf 就可以了。
iconfont
- 首先這個東西估計很多人不知道,我也是在@卓同學的提醒下才知道原來 iOS 也是可以用 iconfont 的。最早這個東西是為 Web 設計的,主要是因為 網頁的 大小直接影響了載入速度,所以在壓縮上連小 icon 都不放過,當然還有一個最主要的目的就是減少請求次數,因為如果是圖片的話,一個圖片就是一次請求。
- 具體的效果可以看一下,使用IconFont減小iOS應用體積這篇文章。
- 雖然看上去效果不多,但對於一些比較追求精緻的公司可以嘗試一下這種方式。
期中,PDF 和 iconfont 兩個都是向量的概念,但是 iconfont 在整個 App 中不管多少種尺寸只需要一個 iconf,但是 PDF 可能需要多個。
HTML 5
- 一些 APP 的一些功能可以用 HTML5 + WebView 的方式來實現。而 HTML 5 這個可以通過下面幾種方式一步步優化:
- 讓做前段的給一個最小的包內建到 App,去除無用程式碼、程式碼混淆壓縮等。
- 將所有圖片 Remote 化。
- 將所有頁面 Remote 化。
其他
- 當然,還要注意資原始檔重複的問題。而資原始檔重複問題主要有幾種:名字相同、名字不同內容相同/相似。
- 對於名字相同的問題,你可以把原來的拖的方式改為.xcassets,他會自動管理相同名字的圖片。然後把多餘的去掉
- 名字不同內容相同/相似:你可以使用Duplicate Photos工具
- 還有一個問題就是資原始檔沒有用,卻佔了空間,可以使用LSUnusedResources將程式碼中沒用到的檔案刪除。當然可能存在誤刪,比如用陣列載入的圖片,這個工具無法識別。
程式碼層面
Install Smallest VS. Coding Fastest
語言選擇
- 雖然說我本人更喜歡用 Swift 來寫 App。但從 App 瘦身的角度,不推薦使用 Swift,不論純 Swift 還是 混編。原因很簡單。看一下下面的圖:
- 這是任何一個包含有 Swift 程式碼的 App 都有的一個為了支援 Swift 的動態庫集合,在10M 左右。如果你使用 Objective - C 完全不用這個東西。
- 當然,我是可以接受安裝包大10M 來用 Swift 寫的?。
資料庫選擇
- 這個問題也是我在分析 Yep 的第三方庫的時候發現的問題,因為 Yep 使用的是 Realm,據說是目前是效能最好的移動端資料庫。但是在三方庫中可以看到,Realm 的支援佔了很大的比重,大約在 8M 左右。但是如果使用 FMDB 話只需要192KB,而 CoreData 幾乎可以忽略不計。下面是部分截圖。
MRC VS. ARC
- 最開始是在Bang的這篇文章中看到用ARC比用 MRC 會導致可執行檔案大10%。起初我是不相信的,但是在我用 SDWebImage 的1.0 版測試之後,ARC 比 MRC 的可執行程式增加了14% +。所以MRC 比 ARC 編譯成可執行檔案之後更小,具體的測試方法可以去他的部落格看,這裡就不重複了。
總結
- 先分析一下前面幾個問題造成的原因:
- Swift && Realm : 首先 Swift 是因為不穩定,所以支援的動態庫沒有整合到系統的"dyld的共享快取"中去。而 Realm 因為不是蘋果自己開發的,所以支援的動態庫也沒有整合到系統的"dyld的共享快取"中去。所以都內建在了 App 中,而且這兩個功能需要寫很多程式碼來實現,因此容量又很大,導致看起來這兩個東西佔了很大的"無用"的容量。(ps.關於iOS 中庫的問題,你可以去我的筆記中檢視~)
- ARC:因為 ARC 叫做自動引用計數,他的實現方式其實就是 Xcode 在編譯的時候自動給你加記憶體管的程式碼,但是機器畢竟沒人聰明,Xcode 會在很多情況下增加很多沒用的程式碼,這也是為什麼 ARC 的底層實現比 MRC 更快,但是實際執行效能上在有些時候卻不及 MRC 的原因,而正因為增加了很多沒用的程式碼,ARC 最終編譯包會比 MRC 大。
- 總結前面的幾個問題,歸根結底於一個問題,那就是Install Smallest VS. Coding Fastest。很多時候為了追求更快的編碼速度,總會有所損失,但是在我看來這些事值得的,不然為什麼我們不用 C 來代替 objective-c 或者用匯編來代替 C 呢??
Bitcode
- bitcode 是被編譯程式的一種中間形式的程式碼。包含 bitcode 配置的程式將會在 App Store 上被編譯和連結。 bitcode 允許蘋果在後期重新優化我們程式的二進位制檔案,而不需要我們重新提交一個新的版本到 App Store 上。
- 當我們提交程式到 App Store上時, Xcode 會將程式編譯為一箇中間表現形式( bitcode )。然後 App store 會再將這個 bitcode 編譯為可執行的64位或32位程式。
- 所以,通過這個方式,我們可以做到架構級別的App Slicing。
Tips
結合上面的內容,再加上Bang大神寫的部落格,我總結了幾條 Tips。排名越往前的我覺得越需要去優化。
Tip 1:去除重複、無用資原始檔,解決名字重複問題。
Tip 2:圖片使用.xcassets管理且無須考慮@1x\@2x\@3x 問題。萬不得已再用拖的辦法,同時結合一定策略方案進行包瘦身。
Tip 3:圖片使用PDF 優先順序高於 PNG,因為 Xcode 會幫你完成剩下的任務。
Tip 4:使用tinypng壓縮PNG圖片。視訊可以通過 Final cut 等軟體進行解析度壓縮。音訊則降低位元速率即可。
Tip 5:icon 使用 iconfont
Tip 6:非必須資源檔案可以放到自己伺服器上, 但必用資原始檔需要內建到安裝包中。
Tip 7:HTML 5 需要將圖片 Remote 化 或者將整個HTML 5 的頁面 Remote化。
Tip 8:Build Settings->Optimization Leve release版應該選擇Fastest, Smalllest
Tip 9:開啟 BitCode
以下是幾乎不可能去做的優化 Tips
Tip 10:儘可能的去除無用的程式碼、控制類名、方法名長度、冗餘字串
Tip 11:如果你想的話,不使用 Swift、不使用 Realm更甚至於儘量不使用 OC ?
Tip 12:MRC 比 ARC 編譯成可執行檔案之後更小。
更多:工作之餘,寫了點筆記,如果需要可以在我的 GitHub 看。
參考文章
- App Thinning
- Confirmed: Objective-C ARC is slow. Don’t use it! (sarcasm off)
- 4 XCODE ASSET CATALOG SECRETS YOU NEED TO KNOW
- 使用IconFont減小iOS應用體積
水平有限,若有錯誤,希望多多指正!coderonevv@gmail.com