
目錄
一:摘要
二:安裝包組成
三:系統(tǒng)優(yōu)化
四:資源優(yōu)化
五:可執(zhí)行文件優(yōu)化
六:編譯器優(yōu)化
七:拓展
一,摘要
眾所周知蘋果對iOS App大小有所限制,如果大于200MB使用蜂窩網(wǎng)絡(luò)下載需要請求許可,對于流量和手機儲存敏感的用戶很不友好,包的大小甚至影響新用戶的轉(zhuǎn)化,所以對于安裝包的瘦身是App發(fā)展和優(yōu)化過程中不可避的問題。
二,安裝包的組成
1,介紹
iOS安裝包就是ipa,ipa是一個壓縮包,用戶下載的就是這個壓縮包,下載完成就會自動解壓,解壓的過程也就是通常所說的安裝過程,把ipa文件后綴改為zip,然后解壓出來再payload中的.app顯示包內(nèi)容就可以查看里面的資源文件。
2,.app里面的主要內(nèi)容
-
_CodeSignature:存放文件的hash列表。里面有一個文件CodeResources,這個文件是一個屬性列表,包含bundle中所有其他文件的列表。這個屬性列表只有一項files,這是一個字典,鍵是文件名,值通常是Base64格式的散列值。如果鍵表示的文件是可選的,那么值本身也是一個字典,這個字典有一個hash鍵和一個optional鍵(布爾值true)。它的作用是用來判斷一個應(yīng)用程序是否完好無損,能夠防止不小心修改或損壞資源文件。
CodeResources.png 一些
bundle文件:bundle是一種標(biāo)準化的層次結(jié)構(gòu),保存了可執(zhí)行代碼以及代碼所需要的資源。bundle文件可以理解為一個資源包,用于儲存圖片,音頻,文本,nib文件等,方便在其他項目中引用包內(nèi)資源。bundle包是靜態(tài)的,不參與編譯,也就意味著bundle包中不能包含可執(zhí)行文件,它僅僅作為資源被解析成為特定的二進制數(shù)據(jù)。從.app包可以了解到,bundle文件基本都是一些SDK制作的。Assets.car: 把放在Assets.xcassets中的圖片(Applcon和LaunchImage這兩種圖片是直接放在包中的)打包后壓縮成一個Assets.car的文件,減小包大小。plist: 一些屬性列表文件png、jpg、mp4、json等資源文件
三,系統(tǒng)優(yōu)化(App Thinning)
1,簡介
App Thinning是App Store和操作系統(tǒng)在安裝iOS或者watchOS的app的時候通過一系列優(yōu)化似的app以最小的合適的大小被安裝到你的設(shè)備上。
2,App Thinning有三種方式
-
App Slicing:會在你向iTunes Connect 上傳App后,對App做切割,創(chuàng)建不同的變體,這樣就可以適用到不同的設(shè)備。 -
Bitcode:針對特定設(shè)備進行包大小優(yōu)化(優(yōu)化不是很明顯)。 -
On-Demand Resources,主要是為游戲多關(guān)卡場景服務(wù)的,它會根據(jù)用戶的關(guān)卡進度下載隨后幾個關(guān)卡的資源,并且已經(jīng)過關(guān)的資源會被刪除,這樣就可以減少初裝App的包大小。
3,使用
- 這里大部分工作其實都是由Xcode和App Store來幫你完成的,我們只需要通過Xcode添加
xcassets目錄,然后將圖片添加進來即可,新建一個文件選擇 Asset Catalog模板,如圖所示:

-
On-Demand Resources就不多說了,提一點, 在iOS9以后Xcode默認開啟了,可以在下圖位置進行設(shè)置。

四,資源優(yōu)化
1,簡介
資源優(yōu)化主要分兩部分,一是資源做無損的壓縮,二是刪除無用的資源,比如圖片、音頻、視頻、配置文件等。
2,圖片壓縮
- 如果圖片資源小于
100k,使用網(wǎng)頁工具 Tinyjpg 或者GUI工具 ImageOptim; - 如果圖片資源大于
100k,轉(zhuǎn)換圖片格式WebP,轉(zhuǎn)換WebP圖片格式工具推薦 iSparta,也可以使用谷歌自己提供的圖片壓縮工具cwebp; - 需要注意的是WebP在cpu消耗和解碼時間上會比png高兩倍,所以有些需要自己綜合考慮權(quán)衡利弊,不要為了優(yōu)化而優(yōu)化。
3,無用資源刪除
- 推薦使用 LSUnusedResources 工具,很方便(需要二次確認,比如如果你把資源名放入
plist文件中,它也會認為沒有使用)。
五,可執(zhí)行文件優(yōu)化
1,簡介
開頭已經(jīng)講過App安裝包主要由資源文件和可執(zhí)行文件(Mach-O)組成,可執(zhí)行文件的大小由代碼量決定,所以要想對可執(zhí)行文件瘦身,首先應(yīng)該做的就是找到并刪除無用的代碼。
2,Link Map文件分析
Xcode build產(chǎn)生的Link Map文件能比較直觀的反映出程序各部分的文件大小情況,對于減少包體積很有幫助。
- 獲取 LinkMap :將 Build Setting 里的
Write Link Map File設(shè)置為Yes,然后指定 Path to Link Map File 的路徑就可以得到每次編譯后的LinkMap文件了。我們只修改一下生成的 Link Map文件的路徑就可以了,后綴名不要修改。

- linkMap 分為三部分
-
Object files:代碼工程中所有文件編譯后的目標(biāo)文件.o;

-
Section: 描述代碼段在生成的Mach-O里面的偏移位置和大小,包括代碼段(__TEXT)和數(shù)據(jù)段(__DATA)的分布情況。

-
Symbols:符號相關(guān)信息,列出了每個方法、類、block以及它們的大小,第一列Address是在文件中偏移的位置,第二列size是大小,第三列File是對應(yīng)上面Object files 中的文件編號,第四列Name是文件名。如下圖與上圖Object files是對應(yīng)的。

通過對LinkMap的分析不但可以統(tǒng)計出所有的方法和類,還能清晰的看到代碼所占包大小的具體分布,從而有針對性的對代碼進行優(yōu)化。(對Symbols分析還可以通過方法的二進制重排來提成App冷啟動速度)。
3,Mach-O文件分析
Mach-O是Mach Object的縮寫,是Mac/iOS上用于儲存程序、庫的標(biāo)準格式。一個簡單的方法獲得該文件->Xcode編譯結(jié)束會生成一個可執(zhí)行程序,查找方法如下圖:

在這個文件夾下找到對應(yīng)的項目文件名/Build/Products/Debug進入目錄就可找到可執(zhí)行文件了。

iOS的方法都是通過objc_msgSend來調(diào)用的,而objc——msgSend在Mach-O文件里是通過__objc_selrefs這個section來獲取selector這個參數(shù)的,所以__objc_selrefs里是被調(diào)用的方法,__objc_classrefs 里是被調(diào)用過的類,__objc_superrefs是被調(diào)用過的super的類,這樣就能找出使用過的類和子類,然后對比Link Map文件就可以找出沒有用到的類和方法了(OC是動態(tài)語言,方法可以在運行時調(diào)用,所以該方法找出的無用代碼需要我們二次確認才可刪除)。

4,使用AppCode
以上可以看到,工作量還是不小的,推薦一個神器 AppCode ,AppCode通過靜態(tài)分析可以快速的幫我們找到?jīng)]用的類以及方法等,使用起來也特別簡單,在菜單欄選擇 code ->Inspect Code就可以了。

分析結(jié)果包括:
- Not implemented methods:沒有實現(xiàn)的方法;
- Key value coding:KVC相關(guān),比如使用KVC訪問了@private修飾的成員變量;
- Unused import statement:無用類引入聲明;
- Unused instance variable :無用的實例變量;
- Unused method:沒有用到的方法;
- Unused class:沒有用到的類;
- Unused property :沒有用到的屬性;
- Unused parameter :無用參數(shù);
- Unused local variable :無用的局部變量;
- Unused value :無用的值;
- Unused macro :無用的宏;
- Unused global declaration :無用全局聲明。
看似AppCode已經(jīng)把所有工作都做完了,其實不然。下面我們再來列舉下AppCode靜態(tài)檢測的問題:
- 引入的第三方庫,比如
JsonModel里面定義了未使用的協(xié)議會被認為是無用的協(xié)議; - 如果子類使用了父類的方法,父類的這個方法不會被認為使用了;
- 通過點的方式使用屬性,該屬性會被認為沒有使用;
- 使用
performSelector方式調(diào)用的方法也檢測不出來,會被判斷為為使用; - 運行時聲明類的情況檢查不出來。比如使用
NSClassFromString方式調(diào)用的類會被檢查出來沒有使用; -
[ [self class] loadData]這樣不指定類名的方式使用的類也會被檢測出來是未使用的類,以及UITableView的自定義Cell使用registerClass,這樣的情況也會被認為這個Cell為使用;
所以:AppCode檢查完成之后,還是需要我們二次確認才能夠刪除。
6,編譯器優(yōu)化
Xcode 支持編譯器層面的一些優(yōu)化選項,這些優(yōu)化選項可以在更快的編譯速度、更小的二進制和更快的執(zhí)行速度之間根據(jù)實際需要選擇合適的方案。
- Strip Linked Product、Make Strings Read-Only、Symbols Hidden by Default 設(shè)置為 YES(
新版Xcode默認打開); - 去掉異常支持,Enable C++ Exceptions、Enable Objective-C Exceptions 設(shè)置為 NO, Other C Flags 添加 -fno-exceptions
(Other C Flags 添加 -fno-exceptions 一般只有對程序運行效率及資源占用比較看重的場合才會使用, 如果要做的話最好連libstdc++和其他所有的的c++庫都用這兩個參數(shù)重新編譯一遍, 否則只是你自己的程序禁用了這兩個特性, 而別的庫依然開著, 效果就大打折扣了); - Strip Debug Symbols During Copy 設(shè)置為YES。這個是將那些拷貝進項目包的第三方庫、資源或者Extension的Debug Symbol去除掉
(只需在Release模式開啟即可,因為開啟后不能對第三方庫進行斷點調(diào)試以及符號化); - Build Settings -> Optimization Level有幾個編譯優(yōu)化選項,release版應(yīng)該選擇Fastest, Smalllest,這個選項會開啟那些不增加代碼大小的全部優(yōu)化,并讓可執(zhí)行文件盡可能小
(新版Xocde默認已經(jīng)打開); - Enable BitCode設(shè)置為YES;
7,拓展
- 上面這些工作做完相信App已經(jīng)很
"潔凈"了,"瘦身"工作基本完成了,特別對于迭代很久的老項目會有很大的成效。 - 對項目進行Archive后會生成
.xcarchive文件,該文件中包含了App、dsYMS以及其他信息,將.xcarchive文件上傳到 App Store Connect后,蘋果會對App中的可執(zhí)行文件進行DRM加密,然后將App壓縮成ipa文件并發(fā)布到App Store。加密對可執(zhí)行文件的大小影響不大,但是它會嚴重影響可執(zhí)行文件的壓縮效率,導(dǎo)致壓縮后的ipa大小增加,也就是我們的安裝包增大。 - 這種加密使用脫殼工具很容易進行解密。Mach-O文件代碼的解密發(fā)生在Mach-O文件被加載的時候,由Mach Loader進行。Mach Loader 會讀取 Mach-O 中的
LC_ENCRYPTION_INFO這條Load Command來判斷可執(zhí)行文件是否加密。 - 所以可以通過
otool -l <binary-path>的命令來查看 Mach-O 是否被加密過。

其中 cryptoff表示加密字段位于文件中偏移 16384個字節(jié);cryptsize表示加密內(nèi)容長度 16515072字節(jié);cryptid表示加密方法為 1,如果為 0表示不加密(平時打包出來的驗證可以發(fā)現(xiàn)都為0,那時因為加密是在上傳App Store時才做的,怎么獲取線上ipa呢,可以參考我另外一篇文章->ipa包獲)。
查看 LC_SEGMENT_64中 __TEXT 段的范圍:

根據(jù)上面的結(jié)果可以算出加密內(nèi)容實際上都位于 __TEXT 中,也就是說蘋果只會對Mach-O文件中的 __TEXT 段加密,而不會對其他段加密,也就是說,只要能把 __TEXT段中的節(jié)移到其他段,就能減少加密范圍,從而使壓縮效率提升,從而減小下載包大小。
一般來講,在App中可執(zhí)行文件占80%的大小,而加密部分可占執(zhí)行文件的 70%左右,加密會影響 60%左右的壓縮率,因此移走該加密部分會提升 34%左右的下載大?。?strong>需要注意的是,iOS13已經(jīng)對下載大小做了優(yōu)化,所以該方案無法再對iOS13及以上的設(shè)備下載大小再做進一步優(yōu)化)。
需要注意的是,__TEXT 段中所有節(jié)都移走對于小型App沒有任何問題,但在較大類型的App,還需要注意 Crash和鏈接失效問題。
文章到這里暫告一段落,如果有什么問題或者不足歡迎指正,如果對你有幫助,記得點贊收藏,謝謝!
