前言
隨著APP的業(yè)務(wù)迭代凶赁,需求累積的越來越多,APP內(nèi)引入的庫(kù)或是各種業(yè)務(wù)功能代碼也累積的越來越多逆甜,APP的包體不可避免的增大虱肄。包體太大對(duì)于APP的推廣,用戶的下載使用意愿都會(huì)有一定的影響交煞,所以說包體瘦身是項(xiàng)目中無可避免的一項(xiàng)性能優(yōu)化咏窿。
首要工作:建立監(jiān)管體系
包體瘦身,并不是一個(gè)單純的技術(shù)項(xiàng)素征,或者說并不是一個(gè)單次的性能優(yōu)化需求集嵌,包體瘦身應(yīng)該是一個(gè)長(zhǎng)期的固定的工作內(nèi)容萝挤,在進(jìn)行實(shí)際的瘦身前,首先應(yīng)該實(shí)現(xiàn)一個(gè)對(duì)當(dāng)前APP大小的完善的監(jiān)控體系根欧,可以按照各個(gè)業(yè)務(wù)模塊怜珍、資源等方式,把整體的大小詳細(xì)的記錄下來凤粗,并且應(yīng)該有一個(gè)長(zhǎng)期的監(jiān)管體系酥泛。
實(shí)現(xiàn)監(jiān)管只要依賴對(duì)打包時(shí)生成的link map 文件。link map 是編譯鏈接時(shí)可以生成的一個(gè)txt文件侈沪,它生成的目的就是幫助程序員分析包體大小揭璃。link map 記錄了每個(gè)方法在當(dāng)前的二進(jìn)制架構(gòu)下占據(jù)的空間。通過分析link map,我們可以了解每個(gè)類甚至每個(gè)方法占據(jù)了多少安裝包空間亭罪。此外瘦馍,link map 還可以分析引進(jìn)的第三方庫(kù)的代碼空間占比,作為一個(gè)性能考察點(diǎn)应役。
Link map File
: 我們編寫的源碼需要經(jīng)過編譯情组、鏈接,最終生成一個(gè)可執(zhí)行文件箩祥。在編譯階段院崇,每個(gè)類會(huì)生成對(duì)應(yīng)的.o文件(目標(biāo)文件)。在鏈接階段袍祖,會(huì)把.o文件和動(dòng)態(tài)庫(kù)鏈接在一起底瓣。Link Map File
就是這樣一個(gè)記錄鏈接相關(guān)信息的純文本文件,里面記錄了可執(zhí)行文件的路徑蕉陋、CPU架構(gòu)捐凭、目標(biāo)文件、符號(hào)等信息凳鬓。在Symbols部分茁肠,我們可以把類編號(hào)相同的size加起來,算出每個(gè)類或庫(kù)占用的大小缩举。在Object files部分根據(jù)類的編號(hào)可以查出對(duì)應(yīng)的類垦梆。分析的結(jié)果對(duì)App安裝包瘦身會(huì)有一些幫助。github上有很多實(shí)現(xiàn)了這樣功能的開源工具仅孩,實(shí)現(xiàn)原理很簡(jiǎn)單托猩,如有需要可自行查找。
監(jiān)管體系的目標(biāo)是把包體大小可視化辽慕,把各個(gè)業(yè)務(wù)占用的大小和占比用圖標(biāo)的方式展示出來站刑,并且每次業(yè)務(wù)庫(kù)發(fā)版的時(shí)候都加上代碼增量的提醒,主工程集成打包的時(shí)候同樣提示整個(gè)APP的代碼增量提醒鼻百。此外绞旅,不斷的查看比對(duì)工程構(gòu)建出來的ipa包摆尝,也是一個(gè)重要且有必要的監(jiān)控步驟,通過iOS_Images_Extractor
來查看Assets.car
因悲,通過MachOView
可以查看編譯后的mach-o
可執(zhí)行文件堕汞。
包體優(yōu)化: 瘦身方案
當(dāng)有了監(jiān)控體系后,我們對(duì)自己APP的大小就有大致概況的了解晃琳。此時(shí)就進(jìn)入到瘦身這一步了讯检。這一步網(wǎng)上的各個(gè)資料相當(dāng)?shù)亩啵@里總結(jié)一些常用的方案卫旱。
總的來說人灼,優(yōu)化的方向主要有四個(gè),資源的優(yōu)化顾翼,Xcode配置項(xiàng)投放,Mach-o文件的優(yōu)化以及業(yè)務(wù)層面的原生功能下線
資源優(yōu)化
主要是圖片資源和代碼源文件。
圖片資源主要優(yōu)化方向是:
-
圖片壓縮
使用更小的圖片格式适贸。常見的各種小Icon,尤其需要多種顏色的圖片采用 iconfont灸芳;webp也可以作為動(dòng)圖使用
對(duì)PNG無損壓縮,對(duì)于包體優(yōu)化機(jī)會(huì)是沒意義的拜姿。因?yàn)閄code在打包時(shí)會(huì)先把PNG圖片解壓成Bitmap烙样,然后自行進(jìn)行圖片壓縮處理,所以圖片壓縮 只能采用有損壓縮的方式蕊肥,對(duì)圖片的源文件進(jìn)行處理谒获。例如頭條分享的方式:RGB with palette
-
圖片云端下載
- On Demand Resource
-
無用圖片刪除,重復(fù)圖片篩選
-
Pods資源管理
業(yè)務(wù)Pods庫(kù)中的資源不用
resources_bundle
的形式引入到主工程壁却,而是直接用resources
方式集成究反,因?yàn)锳PPLE 在構(gòu)建 Assets.car時(shí)有優(yōu)化處理(比如:將若干張小圖片自動(dòng)合并為一張 Packed Image)-
錯(cuò)誤的使用
cocoapods
會(huì)帶來的圖片重復(fù)合并問題,簡(jiǎn)單來說就是Pod庫(kù)中的圖片資源既被當(dāng)做的了零散的PNG資源在主工程中儒洛,又被打進(jìn)了Assets.car的壓縮包中,這樣同一份文件就被引入了兩份狼速。這個(gè)問題最徹底的解決方案是通過解壓IPA以及包內(nèi)的Assets.car琅锻,通過工具對(duì)得到的所有資源圖做對(duì)比排查。
正確的podspec舉例:s.resources = "Pods/Resources/xxx.xcassets"使用 https://github.com/devcxm/iOS-Images-Extractor 來解壓.car 壓縮包
無用代碼清理
無用代碼一般是指 無用類 和 無用方法向胡。業(yè)務(wù)迭代過程中恼蓬,難免會(huì)有業(yè)務(wù)下線的情況,雖然可以從業(yè)務(wù)層面即使下架僵芹,但是代碼層面的查找更詳細(xì)处硬,精確。
MachO文件中的
__DATA
段中有objc_classrefs
和objc_selrefs
段拇派,分別近似于“被使用的類的集合”和“被使用的方法的集合”,同時(shí)也含有所有的類和方法的集合荷辕,通過取差集的方式可以篩選出未被使用的類和方法凿跳。
重復(fù)代碼
重復(fù)代碼是指功能類似的甚至是直接COPY的代碼,項(xiàng)目中的工具類往往是重災(zāi)區(qū)疮方。重復(fù)代碼最好使用第三方工具來排查控嗜,例如 PMD
Xcode 配置項(xiàng)
Xcode 有一些的配置項(xiàng)能夠帶來非常明顯的收益,不過需要結(jié)合項(xiàng)目的實(shí)際情況骡显,不能盲目使用疆栏。
-
LTO,即
Link Time Optimization
蘋果在2016年的WWDC What’s new in LLVM中詳細(xì)介紹了這一功能惫谤。
LTO 帶來的優(yōu)化有:
(1)將一些函數(shù)內(nèi)聯(lián)化
(2)去除了一些無用代碼
(3)對(duì)程序有全局的優(yōu)化作用LTO 的缺點(diǎn):
會(huì)降低編譯鏈接的速度壁顶,因此只建議在打正式包時(shí)開啟;
開啟了LTO之后溜歪,link map的可讀性明顯降低若专,多出了很多數(shù)字開頭的“類”(LTO的全局優(yōu)化導(dǎo)致的),導(dǎo)致我們還經(jīng)常需要手動(dòng)關(guān)閉LTO打包來閱讀link map痹愚。 Compress PNG Files (COMPRESS_PNG_FILES)
Optimization (ASSETCATALOG_COMPILER_OPTIMIZATION) 設(shè)置為space
-
Optimization Level, Release 環(huán)境設(shè)置為: Fastest, Smallest[-Os]富岳,這個(gè)是Xcode默認(rèn)的設(shè)置,這里可以設(shè)置為Oz(Smallest,Aggressive Size Optimizations[-Oz])
Oz 是 Xcode 11 新增的編譯優(yōu)化選項(xiàng)拯腮。WWDC 2019 《What's New in Clang and LLVM》[3] 中對(duì) Oz 有過介紹窖式。Oz 的核心原理是對(duì)重復(fù)的連續(xù)機(jī)器指令外聯(lián)成函數(shù)進(jìn)行復(fù)用,和“內(nèi)聯(lián)函數(shù)”的原理正好相反动壤。因此萝喘,開啟 Oz,能減小二進(jìn)制的大小琼懊,但同時(shí)理論上會(huì)帶來執(zhí)行效率的額外消耗阁簸。對(duì)性能(CPU)敏感的代碼使用需要評(píng)估。
蘋果給的參考數(shù)據(jù)是 4.5% 的包體積收益哼丈。
-
EXPORTED_SYMBOLS_FILE.
Xcode Build Settings 中的
EXPORTED_SYMBOLS_FILE
配置启妹,控制著 Mach-O 中__LINKEDIT
段中 Export Info 的信息。動(dòng)態(tài)鏈接器 dyld 在做符號(hào)綁定時(shí)醉旦,會(huì)讀取被綁定的動(dòng)態(tài)庫(kù)或可執(zhí)行文件的 Export Info 信息饶米,得到一個(gè)符號(hào)對(duì)應(yīng)的實(shí)際調(diào)用地址。如果正在被綁定的符號(hào)车胡,在目標(biāo)動(dòng)態(tài)庫(kù)的 Export Info 中缺失檬输,dyld 則會(huì)拋出異常,表現(xiàn)為 App 崩潰匈棘。 -
Build Settings中去掉異常支持丧慈,Enable C++ Exceptions和Enable Objective-C Exceptions設(shè)置為NO,Other C Flags添加-fno-exceptions
注意:Enable C++ Excptions和Enable Objective-C Exceptions是指項(xiàng)目支持對(duì)錯(cuò)誤的異常處理主卫,比如try catch逃默、throw之類的鹃愤;所以如果項(xiàng)目中使用的有類似的異常處理的,這個(gè)關(guān)閉了之后會(huì)報(bào)錯(cuò)(Cannot use '@try' with Objective-C exceptions disabled)笑旺。包括宏定義中使用的有try{}昼浦、@finally{}之類的,比如@strongify等筒主,如果關(guān)閉了最后打包的時(shí)候也會(huì)報(bào)錯(cuò)关噪。
-fno-exceptions的意思是禁用異常機(jī)制,參考gcc乌妙,同樣使兔,當(dāng)項(xiàng)目中有try thorw的時(shí)候,就不要設(shè)置這個(gè)選項(xiàng)為NO
Build Settings -> Architectures藤韵,Release下設(shè)置為arm64虐沥。去除32位。
Build Settings -> Generate Debug Symbols設(shè)置為NO泽艘。關(guān)閉生成調(diào)試符號(hào)欲险,關(guān)閉后無法生成DSYM,不建議關(guān)閉匹涮。
MACH-O 處理
1天试、二進(jìn)制段壓縮
Mach-O 文件占據(jù)了 Install Size 中很大一部分比例,但并不是文件中的每個(gè)段/節(jié)在程序啟動(dòng)的第一時(shí)間都要被用到然低∠裁浚可以在構(gòu)建過程中將 Mach-O 文件中的這部分段/節(jié)壓縮,然后只要在這些段被使用到之前將其解壓到內(nèi)存中雳攘,就能達(dá)到了減少包大小的效果带兜,同時(shí)也能保證程序正常運(yùn)行。由于蘋果的一些限制吨灭,我們目前只壓縮了
__TEXT,__gcc_except_tab
與__TEXT,__objc_methtype
兩個(gè)節(jié)刚照,然后在_dyld_register_func_for_add_image
的回調(diào)中對(duì)它進(jìn)行解壓。該方案累計(jì)優(yōu)化了 3.5 MB Install Size喧兄。
2无畔、__TEXT 段代碼遷移
安裝包經(jīng)過壓縮后的 Download Size 若超過 200 MB,在蜂窩網(wǎng)絡(luò)下載 App 就會(huì)受到限制繁莹,這對(duì)新增會(huì)有較大影響。在 2020 年下半年特幔,我們探索實(shí)踐了 __TEXT 段遷移技術(shù):在鏈接階段使用
-rename_section
選項(xiàng)將__TEXT,__text
遷移到__BD_TEXT
,__text
咨演,減少蘋果對(duì)可執(zhí)行文件的加密范圍,提升可執(zhí)行文件的壓縮效率蚯斯,從而減少 Download Size薄风。使用該方案我們最終減少了 60 MB 的 Download Size 以及 2 MB 的 Install Size饵较。詳細(xì)的原理可以參考:《今日頭條優(yōu)化實(shí)踐:iOS 包大小二進(jìn)制優(yōu)化,一行代碼減少 60 MB 下載大小》
業(yè)務(wù)層面的原生功能下線
推進(jìn)產(chǎn)品進(jìn)行低收益需求下線
APP隨著多個(gè)版本的迭代遭赂,可能有很多冗余需求循诉,低收益甚至是零收益的需求隱藏在APP內(nèi),可以推動(dòng)產(chǎn)品撇他,也可以內(nèi)部代碼審查茄猫,去尋找代碼量大但是可下線的需求。
動(dòng)態(tài)化
把代碼文件當(dāng)做資源放到云端困肩,在APP啟動(dòng)的合適時(shí)機(jī)去判斷是否需要下載划纽,可以使用一些動(dòng)態(tài)化的語(yǔ)言來實(shí)現(xiàn)這個(gè)功能。不過有一些功能會(huì)要求在本地存放一些兜底文件锌畸,如果說兜底文件的大小本身過大的話勇劣,動(dòng)態(tài)化在包體瘦身方面的作用就是負(fù)的了。是否要采用動(dòng)態(tài)化實(shí)現(xiàn)某些功能潭枣,還是需要具體問題具體分析比默。
參考
https://www.infoq.cn/article/XUJL32hTDKYqAKz0hkMM
https://mp.weixin.qq.com/s?__biz=MzI1MzYzMjE0MQ==&mid=2247484366&idx=1&sn=5a2d0e981c733e9eaec274b835600e67&chksm=e9d0c82cdea7413ada372bb936541c2a49de664b38daa6137c179ab6177231fa8b00ddf9acbc&scene=21#wechat_redirect