記錄一次安裝包大小優(yōu)化的實(shí)踐烂完。
維持安裝包體積是一個(gè)持續(xù)的過(guò)程,建立預(yù)警機(jī)制,監(jiān)控每個(gè)版本的體積大小诫咱。
資源文件優(yōu)化
圖片資源優(yōu)化
- 推薦通過(guò)FengNiao清理無(wú)用的圖片資源 onevcat/FengNiao
- swift開(kāi)發(fā)候味,支持幀動(dòng)畫(huà)圖片匹配刃唤,如image_%d
- 命令行工具,可以給 Xcode 添加 Run Script负溪,在每次構(gòu)建的時(shí)候自動(dòng)檢測(cè)/清理未使用的資源
FengNiao 的基本原理是查找出項(xiàng)目中所有使用到的字符串和項(xiàng)目中所有的資源文件透揣,兩者進(jìn)行匹配(完全匹配和模式匹配,模式匹配支持帶數(shù)字資源的前綴/中綴/后綴匹配)川抡,計(jì)算差集就為未使用的資源辐真。
- 推薦通過(guò)ImageOptim進(jìn)行圖片壓縮 ImageOptim
- 壓縮項(xiàng)目中的JPG格式圖片能明顯減少安裝包大小
- 壓縮項(xiàng)目中Assets內(nèi)的PNG格式圖片無(wú)明顯作用,對(duì)于大圖甚至產(chǎn)生負(fù)優(yōu)化
Xcode會(huì)自動(dòng)壓縮Assets內(nèi)的PNG格式圖片崖堤,但是Bundle內(nèi)的圖片資源仍需手動(dòng)壓縮侍咱。
對(duì)于三方庫(kù)內(nèi)的Bundle內(nèi)的圖片建議放置于Assets中。
- 大圖優(yōu)化
- 切分大圖密幔,將圖片背景楔脯、文案改為代碼實(shí)現(xiàn)
- 動(dòng)效資源圖片采用雪碧圖的方式存儲(chǔ)
- 部分無(wú)透明度且無(wú)法拆分的背景大圖,采用JPG格式并有損壓縮至設(shè)計(jì)師可以接受的范圍內(nèi)
- 圖標(biāo)優(yōu)化
- 通過(guò)修改tintcolor復(fù)用單色重復(fù)圖標(biāo)
- 通過(guò)旋轉(zhuǎn)復(fù)用圖標(biāo)
- 推薦使用On-Demand Resources On-Demand Resources Guide
將 App 中的無(wú)需立即用到資源放在 App Store 云端上胯甩,然后你需要把資源標(biāo)記為不同的Tag昧廷,需要的時(shí)候才去下載相應(yīng)Tag的圖片。如游戲中的不同關(guān)卡資源偎箫。
- 建議將webp作為項(xiàng)目圖片格式
- 壓縮率高木柬。支持有損和無(wú)損2種方式,比如將 Gif 圖可以轉(zhuǎn)換為 Animated WebP淹办,有損模式下可以減小 64%眉枕,無(wú)損模式下可以減小 19%
- 支持 Alpha 透明和 24-bit 顏色數(shù),不會(huì)像 PNG8 那樣因?yàn)樯什粔虺霈F(xiàn)毛邊
- CUP 消耗和解碼時(shí)間上會(huì)比 PNG 高2倍
視頻/音頻資源遠(yuǎn)端化
建議將視頻與音頻文件放置在服務(wù)器怜森,客戶(hù)端按需下載或者使用流播放速挑。
HTML5遠(yuǎn)端化
建議將HTML5資源放置在服務(wù)器,客戶(hù)端可以使用離線(xiàn)緩存的方式來(lái)緩存網(wǎng)頁(yè)資源到本地副硅。
代碼優(yōu)化
- 掃描未使用代碼
- 基于 Clang 掃描 如何使用 Clang Plugin 找到項(xiàng)目中的無(wú)用代碼
基本思路是基于 clang AST姥宝,追溯到函數(shù)的調(diào)用層級(jí),記錄所有定義的方法/類(lèi)和所有調(diào)用的方法/類(lèi)恐疲,再取差集伶授。
- 基于可執(zhí)行文件掃描 iOS微信安裝包瘦身
基本思路是Mach-O 文件中的 (__DATA,__objc_classlist) 段表示所有定義的類(lèi)断序, (__DATA.__objc_classrefs) 段表示所有引用的類(lèi)(繼承關(guān)系是在 __DATA.__objc_superrefs 中);使用的方法和引用的方法也是類(lèi)似原理糜烹。因此我們使用 otool 等命令逆向可執(zhí)行文件中引用到的類(lèi)/方法和所有定義的類(lèi)/方法违诗,然后計(jì)算差集。
- 基于源碼掃描 fui
基本思路是對(duì)源碼文件進(jìn)行字符串匹配疮蹦。例如將 A *a诸迟、[A xxx]、NSStringFromClass("A")愕乎、objc_getClass("A") 等歸類(lèi)為使用的類(lèi)阵苇,@interface A : B 歸類(lèi)為定義的類(lèi),然后計(jì)算差集感论。
- 通過(guò) AppCode 查找無(wú)用代碼 AppCode
查找出 AppCode 中無(wú)用的類(lèi)绅项、無(wú)用的方法甚至是無(wú)用的 import ,但是無(wú)法掃描通過(guò)字符串拼接方式來(lái)創(chuàng)建的類(lèi)和調(diào)用的方法比肄。
- Cocoapods 中的優(yōu)化選項(xiàng)配置
Cocoapods 的 project 文件在每次 pod install 或者 pod update 會(huì)重置快耿,所以需要 hook pod install 來(lái)設(shè)置 Pods 中每個(gè) Target 的編譯選項(xiàng):
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'YES'
config.build_settings['STRIP_INSTALLED_PRODUCT'] = 'YES'
config.build_settings['SWIFT_COMPILATION_MODE'] = 'wholemodule'
config.build_settings['SWIFT_VERSION'] = '5.0'
if config.name == 'Debug'
config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] = '-Onone'
config.build_settings['GCC_OPTIMIZATION_LEVEL'] = '0'
config.build_settings['COPY_PHASE_STRIP'] = 'NO'
config.build_settings['DEPLOYMENT_POSTPROCESSING'] = 'NO'
config.build_settings['GCC_GENERATE_DEBUGGING_SYMBOLS'] = 'YES'
else
config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] = '-Osize'
config.build_settings['GCC_OPTIMIZATION_LEVEL'] = 's'
config.build_settings['COPY_PHASE_STRIP'] = 'YES'
config.build_settings['DEPLOYMENT_POSTPROCESSING'] = 'YES'
config.build_settings['GCC_GENERATE_DEBUGGING_SYMBOLS'] = 'NO'
end
end
end
end
- 靜態(tài)庫(kù)瘦身
發(fā)布版本中刪除 i386、x86_64 是模擬器的指令集芳绩,只保留 armv7 和 arm64
- 靜態(tài)庫(kù)指令集信息查看:lipo -info libname.a(或者libname.framework/libname)
- 靜態(tài)庫(kù)拆分:lipo 靜態(tài)庫(kù)文件路徑 -thin CPU架構(gòu) -output 拆分后的靜態(tài)庫(kù)文件路徑
- 靜態(tài)庫(kù)合并:lipo -create 靜態(tài)庫(kù)1文件路徑 靜態(tài)庫(kù)2文件路徑... 靜態(tài)庫(kù)n文件路徑 -output 合并后的靜態(tài)庫(kù)文件徑
4.iOS的指令集
- 如果項(xiàng)目可以?xún)H支持iphone5s及以上設(shè)備掀亥,去除armv7/armv7s支持
arm64:iPhone6s | iphone6s plus|iPhone6| iPhone6 plus|iPhone5S | iPad Air| iPad mini2(iPad mini with Retina Display)
armv7s:iPhone5|iPhone5C|iPad4(iPad with Retina Display)
armv7:iPhone4|iPhone4S|iPad|iPad2|iPad3(The New iPad)|iPad mini|iPod Touch 3G|iPod Touch4
i386是針對(duì)intel通用微處理器32位處理器
x86_64是針對(duì)x86架構(gòu)的64位處理器
模擬器32位處理器測(cè)試需要i386架構(gòu),
模擬器64位處理器測(cè)試需要x86_64架構(gòu)妥色,
真機(jī)32位處理器需要armv7,或者armv7s架構(gòu)搪花,
真機(jī)64位處理器需要arm64架構(gòu)。
編譯優(yōu)化
- Clang/LLVM 編譯器優(yōu)化選項(xiàng)
Xcode ?? Build Setting ??Apple Clang - Code Generation ?? Optimization Level 設(shè)置為Fastest Smallest[-Os]
- None[-O0]
Debug 默認(rèn)級(jí)別嘹害,不進(jìn)行任何優(yōu)化撮竿,直接將源代碼編譯到執(zhí)行文件中,結(jié)果不進(jìn)行任何重排笔呀,編譯時(shí)比較長(zhǎng)倚聚。主要用于調(diào)試程序,可以進(jìn)行設(shè)置斷點(diǎn)凿可、改變變量 、計(jì)算表達(dá)式等調(diào)試工作授账。 - Fast[-O,O1]
最常用的優(yōu)化級(jí)別枯跑,不考慮速度和文件大小權(quán)衡問(wèn)題。與-O0級(jí)別相比白热,它生成的文件更小敛助,可執(zhí)行的速度更快,編譯時(shí)間更少屋确。 - Faster[-O2]
在-O1級(jí)別基礎(chǔ)上再進(jìn)行優(yōu)化纳击,增加指令調(diào)度的優(yōu)化续扔。與-O1級(jí)別相,它生成的文件大小沒(méi)有變大焕数,編譯時(shí)間變長(zhǎng)了纱昧,編譯期間占用的內(nèi)存更多了,但程序的運(yùn)行速度有所提高堡赔。 - Fastest[-O3]
在-O2和-O1級(jí)別上進(jìn)行優(yōu)化识脆,該級(jí)別可能會(huì)提高程序的運(yùn)行速度,但是也會(huì)增加文件的大小善已。 - Fastest Smallest[-Os]
Release 默認(rèn)級(jí)別灼捂。這種級(jí)別用于在有限的內(nèi)存和磁盤(pán)空間下生成盡可能小的文件。由于使用了很好的緩存技術(shù)换团,它在某些情況下也會(huì)有很快的運(yùn)行速度悉稠。 - Fastest, Aggressive Optimization[-Ofast]
它是一種更為激進(jìn)的編譯參數(shù), 它以點(diǎn)浮點(diǎn)數(shù)的精度為代價(jià)。
- Swift Compiler - Code Generation
Xcode -> Build Setting ->Swift Compiler - Code Generation -> Optimization Level 設(shè)置為Optimize for Size[-Osize]
- No optimization[-Onone]:不進(jìn)行優(yōu)化艘包,能保證較快的編譯速度的猛。
- Optimize for Speed[-O]:編譯器將會(huì)對(duì)代碼的執(zhí)行效率進(jìn)行優(yōu)化,一定程度上會(huì)增加包大小辑甜。
- Optimize for Size[-Osize]:編譯器會(huì)盡可能減少包的大小并且最小限度影響代碼的執(zhí)行效率衰絮。
- Deployment
Xcode ?? Build Setting ?? Deployment
- Strip Linked Product & Deployment Postprocessing
Strip Linked Product設(shè)置為YES,Deployment Postprocessing設(shè)置為NO
在 Archive 的時(shí)候 Xcode 總是會(huì)把 Deployment Postprocessing 設(shè)置為 YES 磷醋。所以我們可以打開(kāi) Strip Linked Product 并且把 Deployment Postprocessing 設(shè)置為 NO猫牡,而不用擔(dān)心調(diào)試的時(shí)候會(huì)影響斷點(diǎn)和符號(hào)化,同時(shí)打包的時(shí)候又會(huì)自動(dòng)去除符號(hào)信息邓线。
- Strip Linked Product/Strip Debug Symbols During
僅在Release環(huán)境設(shè)置為YES
與 Strip Linked Product 類(lèi)似淌友,但是這個(gè)是將那些拷貝進(jìn)項(xiàng)目包的三方庫(kù)、資源或者 Extension 的 Debug Symbol 去除掉骇陈,同樣也是使用的 strip 命令震庭。這個(gè)選項(xiàng)沒(méi)有前置條件,所以我們只需要在 Release 模式下開(kāi)啟你雌,不然就不能對(duì)三方庫(kù)進(jìn)行斷點(diǎn)調(diào)試和符號(hào)化了
去掉不必要的符號(hào)信息器联,可以減少可執(zhí)行文件大小,但去除了符號(hào)信息之后我們就只能使用 dSYM 來(lái)進(jìn)行符號(hào)化婿崭。
- Link-Time Optimization
Xcode ?? Build Setting ??Apple Clang - Code Generation ??Link-Time Optimization設(shè)置為Incremental
Link-Time Optimization 是 LLVM 編譯器的一個(gè)特性拨拓,用于在 link 中間代碼時(shí),對(duì)全局代碼進(jìn)行優(yōu)化氓栈。這個(gè)優(yōu)化是自動(dòng)完成的渣磷,因此不需要修改現(xiàn)有的代碼;這個(gè)優(yōu)化也是高效的授瘦,因?yàn)榭梢栽谌忠暯窍聝?yōu)化代碼醋界。
開(kāi)啟這個(gè)優(yōu)化后竟宋,一方面減少了匯編代碼的體積,一方面提高了代碼的運(yùn)行效率.
- 多余代碼去除(Dead code elimination):如果一段代碼分布在多個(gè)文件中形纺,但是從來(lái)沒(méi)有被使用丘侠,普通的 -O3 優(yōu)化方法不能發(fā)現(xiàn)跨中間代碼文件的多余代碼,因此是一個(gè)“局部?jī)?yōu)化”挡篓。但是Link-Time Optimization 技術(shù)可以在 link 時(shí)發(fā)現(xiàn)跨中間代碼文件的多余代碼婉陷。
- 跨過(guò)程優(yōu)化(Interprocedural analysis and optimization):這是一個(gè)相對(duì)廣泛的概念。舉個(gè)例子來(lái)說(shuō)官研,如果一個(gè) if 方法的某個(gè)分支永不可能執(zhí)行秽澳,那么在最后生成的二進(jìn)制文件中就不應(yīng)該有這個(gè)分支的代碼。
- 內(nèi)聯(lián)優(yōu)化(Inlining optimization):內(nèi)聯(lián)優(yōu)化形象來(lái)說(shuō)戏羽,就是在匯編中不使用 “call func_name” 語(yǔ)句担神,直接將外部方法內(nèi)的語(yǔ)句“復(fù)制”到調(diào)用者的代碼段內(nèi)。這樣做的好處是不用進(jìn)行調(diào)用函數(shù)前的壓棧始花、調(diào)用函數(shù)后的出棧操作妄讯,提高運(yùn)行效率與棧空間利用率
- Asset Catalog Compiler
Xcode ?? Build Setting ??Asset Catalog Compiler ??Optimization設(shè)置為space