從資源和代碼兩方面為App瘦身處理

Git原文:從資源和代碼方面為App瘦身處理


??????對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
Facebook 216.0 256.5M
Twitter 7.47 110.9M
Instagram 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,BitcodeOn-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的時候,大部分工作是由XcodeApp Store來完成的,開發(fā)者本身不需要做任何事情。在開發(fā)中只需要創(chuàng)建xcassets目錄,然后將圖片加入其中即可。在創(chuàng)建一個工程的時候Xcode會默認為開發(fā)者創(chuàng)建一個名為Assets.xcassets的目錄,在開發(fā)過程中只要把圖片資源加入其中即可。

Asssts.xcassets

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

create xcassets

?????? 我們在使用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的使用

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)、在提審前,使用最簡單的方法,通過XcodeFind功能在代碼中搜索圖片資源名稱,如果沒有搜到,說明沒有沒有使用這張圖片,直接刪除圖片資源即可。但是這樣做的比較麻煩,而且效率也比較低;

  • (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打開,

Analyze

Xcode的Analyze靜態(tài)分析,不僅能找到?jīng)]有調(diào)用的代碼,同時還可以找到:

  • 代碼中的野指針或未初始化的變量
  • 內(nèi)存泄漏的代碼
  • 沒有使用過的庫或框架
靜態(tài)分析結(jié)果

我在使用Xcode靜態(tài)分析時,發(fā)現(xiàn)還是有很多問題的,如廢止的文件或類檢查的效果并不是很理想,所以推薦AppCode做靜態(tài)分析。

(2)、使用AppCode做靜態(tài)分析

可以直接使用AppCode來做靜態(tài)分析,操作也很簡單,打開 AppCode --->code--->Inspect Code;

AppCode Inspect Code

在靜態(tài)分析完成之后,可以在Unused code中看到廢棄的代碼,如下圖所示:
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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容