??????對App包瘦身處理是為了減少包的大小,節(jié)約用戶下載App流量。在App Store下載App,如果超過了150MB就必須在Wi-Fi環(huán)境下載或更新,這樣如果``超過了150M,可能就會間接失去了大部分用戶。如果我們的App要兼容iOS7和iOS 8,蘋果官方規(guī)定:主二進制text段的大小不能超過60M,如果超過這個標準,就沒辦法向App Strore提交審核。
??????下面這張表格列舉了當前國內(nèi)外的部分常用App的包大小。
| APP | 版本 | 大小 |
|---|---|---|
| 支付寶 | 10.1.60 | 167.5M |
| 淘寶 | 8.6.10 | 207.6M |
| 微信 | 7.0.3 | 230M |
| 滴滴 | 5.2.45 | 202.4M |
| 抖音 | 5.7.0 | 170.2M |
| 新浪微博 | 9.3.1 | 191.5M |
| 網(wǎng)易云音樂 | 6.1.0 | 147.7M |
| 216.0 | 256.5M | |
| 7.47 | 110.9M | |
| 88.0 | 90.2M |
??????通過上面的表格可以看出目前包少于150M的只有網(wǎng)易云音樂,Instagram,Twitter。那么作為開發(fā)者可以通過哪些方法對自己的App包大小做優(yōu)化處理呢?
1、蘋果提供的 App Thinning
??????我們知道不同的設(shè)備的屏幕大小、分辨率、CPU、GPU、硬盤大小等都不相同。針對這種情況,蘋果官方為開發(fā)者提供了 App Thinning技術(shù),對于不同設(shè)備提供只適用當前設(shè)備的包為用戶提供下載。如iPhone 5C只會下載適用于32位運行芯片的庫和2x的圖片資源的包,iPhone 8P則會下載適用于64位的庫和3x的圖片資源。
蘋果關(guān)于App Thinning的介紹請看:
App Thinning in Xcode視頻
App Thinning官方文檔
(1)、App Thinning有三種方式: App Slicing,Bitcode、On-Demand Resources。
-
App Slicing:iOS9之后蘋果官方出的解決方案,在向iTunes Connect上傳App包后,對App做切割處理,創(chuàng)建不同的變體包,這樣就可以適用到不同的設(shè)備; -
Bitcode:也是iOS9之后蘋果給出的解決方案,針對特定的設(shè)備進行包大小優(yōu)化。 -
On-Demand Resources:主要是為又下多關(guān)卡的情況服務的,On-Demand Resources會根據(jù)用戶的關(guān)卡下載進度下載隨后幾個關(guān)卡的資源,并且已經(jīng)過關(guān)的資源會被干掉,這樣可以減少初始App包的大小。
(2)、那么如何在項目中適用App Thinning呢?
(2.1) 、 Slicing的使用
??????在使用App Slicing的時候,大部分工作是由Xcode和App Store來完成的,開發(fā)者本身不需要做任何事情。在開發(fā)中只需要創(chuàng)建xcassets目錄,然后將圖片加入其中即可。在創(chuàng)建一個工程的時候Xcode會默認為開發(fā)者創(chuàng)建一個名為Assets.xcassets的目錄,在開發(fā)過程中只要把圖片資源加入其中即可。

當然也可以自己新建,選中工程目錄New File--->Resources--->Asset Catalog即可新建一個。

?????? 我們在使用xcassets時,添加對應的2x分辨率和3x分辨率的圖片,會在上傳到App Store后被創(chuàng)建成不同安裝包以減少App安裝包的大小。同時蘋果會自動根據(jù)的設(shè)備不同,在創(chuàng)建的安裝包中自動加入當前設(shè)備需要的芯片指令集架構(gòu)文件。
(2.2) 、Bitcode使用
什么是Bitcode?Apple Developer Bitcode說明
??????bitcode是介于源碼和機器碼之間,在LLVM中的IR(Intermediate Representation)這層,由編譯器前端clang生成,交給LLVM優(yōu)化器優(yōu)化后交給后端生成CPU相應指令。
一般情況下,在iOS9之后,新建一個工程,Xcode會默認設(shè)置Bitcode。由Xcode將Bitcode提供給Apple,由他們針對各種機型(包括新機型)的CPU對代碼做二次優(yōu)化,其本身不會增加包體積,上傳的二進制文件會變大,但是不會影響用戶的下載大小。
如果開啟Bitcode,相應的,所有使用的pod都需要開啟Bitcode,Bitcode必須是完整的,否則就是無效的,編譯階段就會報錯。
開啟Bitcode選項后,在debug模式下不會打進Bitcode,只有在Archive時才會。
(2.3) 、On-Demand Resources的使用
將圖片、音頻等資源文件分離出來,開發(fā)階段將資源按照ResourceTag區(qū)分,存放在蘋果的服務器上。App按需要發(fā)起請求,由操作系統(tǒng)來管理下載和存儲,一般用于游戲資源或者內(nèi)購。
優(yōu)點:
- 包體積更小,為設(shè)備省更多的存儲控件
- 懶加載(Game -> level -> download current resources)
- 很少使用的資源可以放在服務器(Tutorial)
缺點:
- 資源需要從蘋果服務器下載
- 資源需要按tag區(qū)分,制定相應的配置策略
- 代碼中管理何時下載,何時釋放,增加了資源管理的復雜度
2、刪除廢止、無用的資源
?????? 在App的版本迭代中,我們不斷的加入新的資源。但是由于后期的需求、設(shè)計、交互等變更,前期加的一些資源就沒有用了,但是當時并沒有刪除這些資源,導致我們的資源越來越臃腫,App包越來越大。如我們的App中加入了圣誕版本的素材,但是這個版本之后這些素材就沒用了。而刪除這些無用資源也是最簡單最有效的能為App包大小帶來最明顯的效果的操作,那么,該如找到并刪除這些無用的資源呢?
(1)、在提審前,使用最簡單的方法,通過
Xcode的Find功能在代碼中搜索圖片資源名稱,如果沒有搜到,說明沒有沒有使用這張圖片,直接刪除圖片資源即可。但是這樣做的比較麻煩,而且效率也比較低;(2)、通過
Mac終端Find命令來搜索工程中的所有圖片資源,確認是無用的圖片資源后刪除,此處可以使用系統(tǒng)提供的NSFileManger類來刪除;(3)、此處為各位推薦一個開源工具LSUnusedResources,
LSUnusedResources不僅擁有GUI,而且添加和刪除操作也很簡單。(4)、刪除一些其他的文件,如在我們的項目中使用了lottie動畫,所以在本地會有一些對應的
json文件,對于廢止的文件,可以通過上面的方法刪除;
3、圖片資源壓縮處理
試想,如果設(shè)計師給的圖片非常大,一張圖片達到了幾十KB,甚至是幾MB,如果這樣的資源很多,那么最終打包下來的安裝包自然就很大,如在我們的項目中設(shè)計師給出的一張全屏背景圖僅1x的圖片就達到了53KB,這樣對圖片的壓縮處理就顯得刻不容緩了。

我們要的目的是為了在不損失圖片質(zhì)量的情況下盡可能大的對圖片做壓縮處理,這樣就能節(jié)省一些空間,減少安裝包的大小。此處推薦Google的方案將圖片轉(zhuǎn)成WebP(這里需要梯子才能打開)。
(1)、為什么要使用WebP
-
WebP壓縮率高,肉眼基本看不出差異,支持有損和無損兩種模式,壓縮之后不僅不損失圖片質(zhì)量,官方提出相對于我們常用的PNG格式能減少26%的大小。 -
WebP支持Alpha透明和24-bit顏色數(shù),不會像PNG8那樣因為色彩不夠出現(xiàn)毛邊; -
WebP兼容目前主流的瀏覽器和平臺;
(2)、WebP的安裝
使用Homebrew命令安裝webp,在終端輸入:
brew install webp
如我要把all_background.png這張圖片采用無損壓縮方式轉(zhuǎn)成WebP可使用命令:
cwebp -lossless all_background.png -o all_background.webp
如果要控制壓縮質(zhì)量為50%:
cwebp -q 50 girl006.jpg -o girl006.webp
壓縮完成之后,只占了3K內(nèi)存,優(yōu)化效果非常明顯。

(3)、WebP的使用
WebP圖片的加載可參考:WebP解碼支持(Objc & Swift)
let path : String = Bundle.main.path(forResource: "all_background", ofType: "webp")!;
self.backgroundImg.image = UIImage.init(webPPath: path);
關(guān)于WebP的更多使用方式可參考WebP官方網(wǎng)站。
4、刪除廢止的代碼文件
在產(chǎn)品的不斷更新和迭代中,代碼中不可避免的會刪除或廢止一些功能,但是作為開發(fā)者有時候并沒有及時的刪除這些廢止的代碼。這樣隨著代碼量的增加,APP中的冗余或無效代碼就越來越多,我們對代碼瘦身的目的就是找出這些代碼并刪除。那么可以通過哪些方法來達到目的呢?
(1)、使用Xcode的Analyze靜態(tài)分析
打開Xcode,Product --> Analyze,也可以使用快捷鍵 command ? + ? + B打開,

Xcode的Analyze靜態(tài)分析,不僅能找到?jīng)]有調(diào)用的代碼,同時還可以找到:
- 代碼中的野指針或未初始化的變量
- 內(nèi)存泄漏的代碼
- 沒有使用過的庫或框架

我在使用Xcode靜態(tài)分析時,發(fā)現(xiàn)還是有很多問題的,如廢止的文件或類檢查的效果并不是很理想,所以推薦AppCode做靜態(tài)分析。
(2)、使用AppCode做靜態(tài)分析
可以直接使用AppCode來做靜態(tài)分析,操作也很簡單,打開 AppCode --->code--->Inspect Code;

在靜態(tài)分析完成之后,可以在
Unused code中看到廢棄的代碼,如下圖所示:
-
Unused class是廢棄類 -
Unused import statement是無用類引用申明 -
Unused property是無用的屬性 -
Unused method是無用的方法 -
Unused parameter是無用的參數(shù) -
Unused instance variable是無用的實例變量 -
Unused local variable是無用的局部變量 -
Unused value是無用的值 -
Unused macro是無用的宏定義 -
Unused global decaration是無用的全局申明
那么問題來了,使用AppCode靜態(tài)分析就可以檢查出所有的廢棄代碼了嗎?并不是,下面列舉了幾種情況,AppCode認為方法或者類沒有調(diào)用:
- 當使用
objc_msgSend或@selector調(diào)用一個方法時 - 利用點的方式使用屬性時
- 父類申明在子類中實現(xiàn)的方法時
- 使用
NSClassFromString運行時方式申明的類
UIViewController *TestVC = [[NSClassFromString(@"TestClassViewController") alloc]init];
SEL aSelector = NSSelectorFromString(@"setIsPlayLaunchAnimation:");
if ([TestVC respondsToSelector:aSelector]) {
IMP aIMP = [SearchMainView methodForSelector:aSelector];
void (*setter)(id, SEL, BOOL) = (void(*)(id, SEL, BOOL))aIMP;
setter(TestVC, aSelector,true);
}
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:TestVC];
所以,使用工具靜態(tài)分析并不能完美的找出所有的廢棄代碼,在刪除代碼前要手動確認該方法或類是否調(diào)用了。最簡單的方式就是利用Xcode自帶的Find功能。
(3)、刪除工程中的野雞開源代碼
有很多優(yōu)秀的同行,他們分享了自己寫的代碼,這使得一些程序員在開發(fā)中遇到一個需求首先想到的是去GitHub、CSDN、Code4App上去找demo,而不是自己根據(jù)需求造輪子,找到比較相近的代碼改一改就直接加到自己的代碼中使用了,但是這樣會產(chǎn)生很多不可控的問題。例如:
- 后期需求變更
- 系統(tǒng)版本升級之后的兼容問題
- 出現(xiàn)BUG給作者提
Issues不能及時得到回復或解決 - 代碼作者長期沒有維護
我個人建議,在使用第三方的開源代碼的時候一定要慎重,不要隨便找一個開源代碼就加到自己的工程中使用,可以借鑒作者的思路自己造輪子,當然對于Star很多且作者在長期維護的代碼可以選擇使用。同時GitHub上優(yōu)秀的代碼大部分都是都系統(tǒng)API的封裝,如:
-
Alamofire、AFNetWorking是對URLSession的進一步封裝, -
SnapKit、Masonry是對AutoLayout的封裝,
當然有些功能或API是蘋果沒有為開發(fā)者提供的,我們就不得不使用第三方的開源代碼,如
- iOS 6之前蘋果并沒有提供
UICollectionView,所以開發(fā)者們只能使用第三方的開源代碼, - iOS 7之前蘋果沒有提供
UITableViewCell自適應高度,所以我們只能使用UITableView+FDTemplateLayoutCell
個人建議:如果有系統(tǒng)提供的API,優(yōu)先使用系統(tǒng)的方法或API。
在我們開發(fā)團隊最新的工程中,我們并沒有使用SnapKit,而是使用了蘋果提供的NSLayoutAnchor,個人覺得蘋果經(jīng)過從iOS 9之后的不斷優(yōu)化,已經(jīng)基本能夠滿足大部分不太復雜的APP的需求了。
本文主要介紹了在iOS開發(fā)中如何從圖片資源和代碼兩方面為App瘦身的方法,如果你有更優(yōu)更好的解決方法,歡迎指點。
本文參考:
包大小:如何從資源和代碼層面實現(xiàn)全方位瘦身?
Apple Developer App Thinning in Xcode