iOS 應(yīng)用包大小瘦身解決辦法

本文是借鑒 戴銘老師 iOS開發(fā)高手課 內(nèi)容總結(jié)。

App 的安裝包主要是由資源和可執(zhí)行文件組成的。

App瘦身:無(wú)用圖片、代碼刪除 + 圖片壓縮

目錄

1、蘋果?App Thinning 功能介紹

2、刪除無(wú)用圖片 方法?

3、谷歌Webp圖片資源壓縮

4、騰訊公司開發(fā)的iSparta? 工具進(jìn)行圖片壓縮。

5、代碼瘦身:對(duì)可執(zhí)行文件的瘦身方法。(人工查找)

6、LinkMap 結(jié)合 Mach-O 找無(wú)用代碼

7、通過(guò) AppCode 找出無(wú)用代碼

8、通過(guò) ObjC 的 runtime 源碼,我們可以找到怎么判斷一個(gè)類是否初始化過(guò)的函數(shù)


1、充分利用 蘋果官方自帶 App Thinning 功能

1、iOS設(shè)備屏幕尺寸、分辨率越來(lái)越多,需要更多的資源匹配不同的尺寸和分辨率。而App Thinning會(huì)選擇可用于當(dāng)前設(shè)備的資源內(nèi)容進(jìn)行下載。iPhone6只會(huì)下載2x圖片,iPhone6plus只會(huì)下載3x圖片。并且下載適合自己設(shè)備的指令集架構(gòu)文件。

2、App Thinning 有三種方式:

? ? ? ?App Slicing:會(huì)在iTunes Connect上傳App后,對(duì)App進(jìn)行切割,創(chuàng)建不同的變體,適用不同的設(shè)備

? ? ? ?On-Demand Resources:主要為游戲關(guān)卡場(chǎng)景服務(wù)的。它會(huì)根據(jù)用戶的關(guān)卡進(jìn)度下載隨后幾個(gè)關(guān)卡的資源,并且已經(jīng)過(guò)關(guān)的資源也會(huì)被刪掉,這樣可以減少初裝App的包大小

? ? ? Bitcode:是針對(duì)特定設(shè)備進(jìn)行包大小優(yōu)化,優(yōu)化不明顯。

3、使用:Xcode 和 App Store已經(jīng)幫我們完成大部分工作。我們通過(guò)Xcode添加 xcassets目錄,然后將圖片添加到這個(gè)目錄既可。添加2x 和 3x分辨率圖片。


2、無(wú)用圖片資源優(yōu)化:刪除無(wú)用圖片 和 圖片資源壓縮

1、刪除無(wú)用圖片:?

? ? ? ?1> 通過(guò)find 命令獲取App安裝包中所有的資源文件。比如 find /Users/..../工程文件

? ? ? ?2>設(shè)置用到的資源的類型,比如jpg、gif、png、webp

? ? ? ?3>使用正則匹配在源碼中找出使用到的資源名,比如 pattern = @"@"(.+?)""

? ? ? ?4>使用find命令找到的所有資源文件,再去掉代碼中使用到的資源文件,剩下的就是無(wú)用資源了

? ? ? ?5>對(duì)于按照規(guī)則設(shè)置的資源名,我們需要在匹配使用資源的正則表達(dá)式里添加相應(yīng)的規(guī)則,比如 @"imgge_%d"

? ? ? ?6>確認(rèn)無(wú)用資源后,就可以對(duì)這些無(wú)用資源執(zhí)行刪除操作了。這個(gè)刪除操作,你可以使用NSFileManager系統(tǒng)類提供的功能來(lái)完成。


2、使用開源的工具來(lái)完成以上操作:LSUnusedResources??特別是對(duì)于使用編號(hào)規(guī)則的圖片來(lái)說(shuō),可以通過(guò)直接添加規(guī)則來(lái)處理。使用方式也很簡(jiǎn)單


LSUnusedResources使用


3、谷歌Webp圖片資源壓縮:對(duì)于 App 來(lái)說(shuō),圖片資源總會(huì)在安裝包里占個(gè)大頭兒。對(duì)它們最好的處理,就是在不損失圖片質(zhì)量的前提下盡可能地作壓縮。目前比較好的壓縮方案是,將圖片轉(zhuǎn)成 WebP。WebP 是 Google 公司的一個(gè)開源項(xiàng)目。

1、WebP特點(diǎn):

? ? ? ? ? 1>WebP 壓縮率高,而且肉眼看不出差異,同時(shí)支持有損和無(wú)損兩種壓縮模式。比如,將 Gif 圖轉(zhuǎn)為 Animated WebP ,有損壓縮模式下可減少 64% 大小,無(wú)損壓縮模式下可減少 19% 大小。

? ? ? ? ? 2>WebP 支持 Alpha 透明和 24-bit 顏色數(shù),不會(huì)像 PNG8 那樣因?yàn)樯什粔蚨霈F(xiàn)毛邊。

2、圖片轉(zhuǎn)成 WebP 操作:

Google 公司在開源 WebP 的同時(shí),還提供了一個(gè)圖片壓縮工具 cwebp?來(lái)將其他圖片轉(zhuǎn)成 WebP。cwebp 使用起來(lái)也很簡(jiǎn)單,只要根據(jù)圖片情況設(shè)置好參數(shù)就行。


cwebp 語(yǔ)法如下:cwebp [options] input_file -o output_file.webp

比如,你要選擇無(wú)損壓縮模式的話,可以使用如下所示的命令:cwebp -lossless original.png -o new.webp


其中,-lossless 表示的是,要對(duì)輸入的 png 圖像進(jìn)行無(wú)損編碼,轉(zhuǎn)成 WebP 圖片。不使用 -lossless ,則表示有損壓縮。


在 cwebp 語(yǔ)法中,還有一個(gè)比較關(guān)鍵的參數(shù) -q float。圖片色值在不同情況下,可以選擇用 -q 參數(shù)來(lái)進(jìn)行設(shè)置,在不損失圖片質(zhì)量情況下進(jìn)行最大化壓縮:

? ? ? ? ? ? ? ? ? 小于 256 色適合無(wú)損壓縮,壓縮率高,參數(shù)使用 -lossless -q 100;

? ? ? ? ? ? ? ? ? 大于 256 色使用 75% 有損壓縮,參數(shù)使用 -q 75;

? ? ? ? ? ? ? ? ? 遠(yuǎn)大于 256 色使用 75% 以下壓縮率,參數(shù) -q 50 -m 6。


4、騰訊公司開發(fā)的iSparta? 工具進(jìn)行圖片壓縮。

1、優(yōu)點(diǎn):iSpart 是一個(gè) GUI 工具,操作方便快捷,可以實(shí)現(xiàn) PNG 格式轉(zhuǎn) WebP,同時(shí)提供批量處理和記錄操作配置的功能。如果是其他格式的圖片要轉(zhuǎn)成 WebP 格式的話,需要先將其轉(zhuǎn)成 PNG 格式,再轉(zhuǎn)成 WebP 格式。

圖片壓縮完了并不是結(jié)束,我們還需要在 ?「顯示圖片」時(shí)使用 libwebp 進(jìn)行解析。這里有一個(gè) iOS 工程使用 ?libwebp 的范例,你可以點(diǎn)擊 這個(gè)鏈接?查看?;蛘呤褂?pod 'SDWebImageWebPCoder' ?框架或者 ?YYimage 框架。Webp圖片加載使用方法

2、缺點(diǎn):WebP 在 CPU 消耗和解碼時(shí)間上會(huì)比 PNG 高兩倍。所以,我們有時(shí)候還需要在性能和體積上做取舍。

? ? ? 建議:如果圖片大小超過(guò)了 100KB,你可以考慮使用 WebP;而小于 100KB 時(shí),你可以使用網(wǎng)頁(yè)工具 TinyPng?或者 GUI 工具 ImageOptim?進(jìn)行圖片壓縮。這兩個(gè)工具的壓縮率沒(méi)有 WebP 那么高,不會(huì)改變圖片壓縮方式,所以解析時(shí)對(duì)性能損耗也不會(huì)增加。

iSpart工具界面
webp圖片加載使用


5、代碼瘦身:對(duì)可執(zhí)行文件的瘦身方法。

1、可執(zhí)行文件就是 Mach-O 文件,其大小是由代碼量決定的。通常情況下,對(duì)可執(zhí)行文件進(jìn)行瘦身,就是 ?「找到并刪除無(wú)用代碼」的過(guò)程。

2、思路:

? ? ? ? ? ?1>首先,找出方法和類的全集;

? ? ? ? ? ?2>然后,找到使用過(guò)的方法和類;

? ? ? ? ? ?3>接下來(lái),取二者的差集得到無(wú)用代碼;

? ? ? ? ? ?4>最后,由人工確認(rèn)無(wú)用代碼可刪除后,進(jìn)行刪除即可。


6、LinkMap 結(jié)合 Mach-O 找無(wú)用代碼

1、怎么快速找到方法和類的全集:我們可以通過(guò)分析 LinkMap 來(lái)獲得所有的代碼類和方法的信息。獲取 LinkMap 可以通過(guò)將 Build Setting 里的 Write Link Map File 設(shè)置為 Yes,然后指定 Path to Link Map File 的路徑就可以得到每次編譯后的 LinkMap 文件了。

LinkMap 文件獲取方法

2、LinkMap 文件分為三部分:Object File、Section 和 Symbols。

其中:1>Object File 包含了代碼工程的所有文件;

? ? ? ? ? ? 2>Section 描述了代碼段在生成的 Mach-O 里的偏移位置和大小;

? ? ? ? ? ? 3>Symbols 會(huì)列出每個(gè)方法、類、block,以及它們的大小。

通過(guò) LinkMap ,你不光可以統(tǒng)計(jì)出所有的方法和類,還能夠清晰地看到代碼所占包大小的具體分布,進(jìn)而有針對(duì)性地進(jìn)行代碼優(yōu)化。

3、得到了代碼的全集信息以后,我們還需要找到已使用的方法和類,這樣才能獲取到差集,找出無(wú)用代碼。所以接下來(lái),我們要 通過(guò) Mach-O 取到使用過(guò)的方法和類。

iOS 的方法都會(huì)通過(guò) objc_msgSend 來(lái)調(diào)用。而,objc_msgSend 在 Mach-O 文件里是通過(guò) __objc_selrefs 這個(gè) section 來(lái)獲取 selector 這個(gè)參數(shù)的。

所以,__objc_selrefs 里的方法一定是被調(diào)用了的。__objc_classrefs 里是被調(diào)用過(guò)的類,__objc_superrefs 是調(diào)用過(guò) super 的類。通過(guò) __objc_classrefs 和 __objc_superrefs,我們就可以找出使用過(guò)的類和子類。那么,Mach-O 文件的 __objc_selrefs、__objc_classrefs 和 __objc_superrefs 怎么查看呢?

我們可以使用 MachOView?這個(gè)軟件來(lái)查看 Mach-O 文件里的信息。MachOView 同時(shí)也是一款開源軟件,如果你對(duì)源碼感興趣的話,可以點(diǎn)擊這個(gè)地址查看。

具體的查看方法,我將通過(guò)一個(gè)案例和你展開。

? ? ? ? ? ? ? ? ?1>首先,我們需要編譯一個(gè) App。

? ? ? ? ? ? ? ? ?2>然后,將生成的 DemoXXX.app 包解開,取出DemoXXX。

? ? ? ? ? ? ? ? ?3>最后,我們就可以使用 MachOView 來(lái)查看 Mach-O 里的信息了。

Link-Map
MachOView 軟件打開樣本

可以看到 __objc_selrefs、__objc_classrefs 和、__objc_superrefs 這三個(gè) section。但是,這種查看方法并不是完美的,還會(huì)有些問(wèn)題。原因在于, Objective-C 是門動(dòng)態(tài)語(yǔ)言,方法調(diào)用可以寫成在運(yùn)行時(shí)動(dòng)態(tài)調(diào)用,這樣就無(wú)法收集全所有調(diào)用的方法和類。所以,我們通過(guò)這種方法找出的無(wú)用方法和類就只能作為參考,還需要二次確認(rèn)。

(缺點(diǎn):這里只能看到已調(diào)用的方法,得去工程里查找為調(diào)用的方法,比較麻煩。建議結(jié)合 下面 的APPCode方法來(lái)進(jìn)行過(guò)濾 找到 未使用的方法、類)


7、通過(guò) AppCode 找出無(wú)用代碼(當(dāng)代碼量過(guò)百萬(wàn)行時(shí) AppCode 的靜態(tài)分析會(huì)“歇菜”。但是,如果工程量不是很大的話,我還是建議你直接使用 AppCode 來(lái)做分析。)

1、AppCode 做分析的方法很簡(jiǎn)單,直接在 「AppCode 里選擇 Code->Inspect Code 」就可以進(jìn)行靜態(tài)分析。

AppCode 來(lái)做靜態(tài)分析
Unused code 里看到所有無(wú)用代碼

2、接下來(lái),說(shuō)一下這些無(wú)用代碼的主要類型。

? ? ? ? 1>無(wú)用類:Unused class 是無(wú)用類,Unused import statement 是無(wú)用類引入聲明,Unused property 是無(wú)用的屬性;

? ? ? ? ?2>無(wú)用方法:Unused method 是無(wú)用的方法,Unused parameter 是無(wú)用參數(shù),Unused instance variable 是無(wú)用的實(shí)例變量,Unused local variable 是無(wú)用的局部變量,Unused value 是無(wú)用的值;

? ? ? ? ?3>無(wú)用宏:Unused macro 是無(wú)用的宏。

? ? ? ? ?4>無(wú)用全局:Unused global declaration 是無(wú)用全局聲明。?

【這里結(jié)合 上面工具 來(lái)進(jìn)行篩選 ——> 沒(méi)有用到的無(wú)用類、方法。】??!

3、APPCode靜態(tài)檢查的問(wèn)題:

? ? ? ? ?1>JSONModel 里定義了未使用的協(xié)議會(huì)被判定為無(wú)用協(xié)議;

? ? ? ? ? 2>如果子類使用了父類的方法,父類的這個(gè)方法不會(huì)被認(rèn)為使用了;

? ? ? ? ? 3>通過(guò)點(diǎn)的方式使用屬性,該屬性會(huì)被認(rèn)為沒(méi)有使用;使用 performSelector 方式調(diào)用的方法也檢查不出來(lái),比如 self performSelector:@selector(arrivalRefreshTime);?

? ? ? ? ? ?4>運(yùn)行時(shí)聲明類的情況檢查不出來(lái)。比如通過(guò) NSClassFromString 方式調(diào)用的類會(huì)被查出為沒(méi)有使用的類,比如 layerClass = NSClassFromString(@“SMFloatLayer”)。還有以[[self class] accessToken] 這樣不指定類名的方式使用的類,會(huì)被認(rèn)為該類沒(méi)有被使用。像 UITableView 的自定義的 Cell 使用 registerClass,這樣的情況也會(huì)認(rèn)為這個(gè) Cell 沒(méi)有被使用。

基于以上種種原因,使用 AppCode 檢查出來(lái)的無(wú)用代碼,還需要人工二次確認(rèn)才能夠安全刪除掉。


4、即使你使用 LinkMap 結(jié)合 Mach-O 或者 AppCode 的方式,通過(guò)靜態(tài)檢查已經(jīng)找到并刪除了無(wú)用的代碼,那么就能說(shuō)包里完全沒(méi)有無(wú)用的代碼了嗎?

實(shí)際上,在 App 的不斷迭代過(guò)程中,新人不斷接手、業(yè)務(wù)功能需求不斷替換,會(huì)留下很多無(wú)用代碼。這些代碼在執(zhí)行靜態(tài)檢查時(shí)會(huì)被用到,但是線上可能連這些老功能的入口都沒(méi)有了,更是沒(méi)有機(jī)會(huì)被用戶用到。也就是說(shuō),這些無(wú)用功能相關(guān)的代碼也是可以刪除的。

8、通過(guò) ObjC 的 runtime 源碼,我們可以找到怎么判斷一個(gè)類是否初始化過(guò)的函數(shù)

1、isInitialized 的結(jié)果會(huì)保存到元類的 class_rw_t 結(jié)構(gòu)體的 flags 信息里,flags 的 1<<29 位記錄的就是這個(gè)類是否初始化了的信息。而 flags 的其他位記錄的信息,你可以參看 objc runtime 的源碼,

2、lags 采用位方式記錄布爾值的方式,易于擴(kuò)展、所用存儲(chǔ)空間小、檢索性能也好。所以,經(jīng)常閱讀優(yōu)秀代碼,特別有助于提高我們自己的代碼質(zhì)量。

既然能夠在運(yùn)行中看到類是否初始化了,那么我們就能夠找出有哪些類是沒(méi)有初始化的,即找到在真實(shí)環(huán)境中沒(méi)有用到的類并清理掉。


總結(jié):對(duì)于上線時(shí)間不長(zhǎng)的新 App 和那些代碼量不大的 App 來(lái)說(shuō),做些資源上的優(yōu)化,再結(jié)合使用 AppCode 就能夠有很好的收益。而且把這些流程加入工作流后,日常工作量也不會(huì)太大。但是,對(duì)于代碼量大,而且業(yè)務(wù)需求迭代時(shí)間很長(zhǎng)的 App 來(lái)說(shuō),包大小的瘦身之路依然任道重遠(yuǎn),這個(gè)領(lǐng)域的研究還有待繼續(xù)完善。LinkMap 加 Mach-O 取差集的結(jié)果也只能作為參考,每次人工確認(rèn)的成本是非常大的,只適合突擊和應(yīng)急清理時(shí)使用。最后日常采用的方案,可能還是用運(yùn)行時(shí)檢查類的方式,這種大粒度檢查的方式精度雖然不高,但是人工工作量會(huì)小很多。

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

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

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