一、背景
隨著業(yè)務的快速發(fā)展與持續(xù)迭代酪耳,玩物得志APP的包體積也在不斷增加浓恳,在僅僅四個月的時間,由V3.0.2的127.4M 增大到V3.5.0的174.5M碗暗,上漲了約37%颈将,可想而知,如果不及時管控言疗,包體積很快會突破200M晴圾。
安裝包過大,將會影響下載轉化率噪奄。google開發(fā)者大會上公布的統(tǒng)計數(shù)據(jù)顯示:
包體大小每上升 6MB死姚,應用下載轉化率就會下降 1%,
而每當包體大小減少 10MB 的時候勤篮,平均下載轉化率也會有 0.5-1.5% 的增長都毒。
安裝包大小有下載大小和安裝大小兩個概念。
下載大小:通過網(wǎng)絡下載的壓縮 App 大小碰缔。為了節(jié)省流量账劲,用戶下載的都是壓縮包,而解壓的過程也就是我們說的安裝手负。
安裝大小:為 App解壓后將在用戶設備上占用的磁盤空間大小涤垫。也就是在App Store上看到的大小,安裝大小較大竟终,通常會影響用戶的下載意愿蝠猬。
下載大小過大,蘋果會限制用戶使用蜂窩網(wǎng)絡下載App统捶。
2017 年 9 月榆芦,iOS 11 后柄粹,下載限制從 100 MB 提升至 150 MB
2019 年 5 月,下載限制從 150 MB 提升至 200 MB
2019 年 9 月匆绣,iOS 13 后驻右,若下載大小超過 200 MB,用戶可選擇是否使用蜂窩網(wǎng)絡下載崎淳,但iOS 13以下的系統(tǒng)仍然無法通過蜂窩網(wǎng)絡下載
雖然蘋果在逐漸放寬限制堪夭。但下載大小若超出 200 MB,可以肯定對APP下載成本拣凹,推廣效率都會產(chǎn)生比較大的影響森爽。
而安裝大小過大,是會影響用戶的留存率的嚣镜,畢竟當用戶手機內(nèi)存不夠用時爬迟,肯定是優(yōu)先刪除占內(nèi)存比較大的App。
所以降低下載大小和安裝大小就是我們的目的菊匿。
二付呕、包大小分析
通過解壓一個ipa文件,我們可以看到一個.app文件中主要包括三個部分:
資源文件:主要是圖片跌捆、音頻徽职、視頻、等資源疹蛉。
可執(zhí)行文件:程序的主體活箕,是將我們的代碼、靜態(tài)庫可款、動態(tài)庫通過編譯鏈接生成的文件育韩。
bundle:工程中使用的三方或資源bundle。
不過.app的大小并不完全就是包體積的大小闺鲸,在APP上傳到 AppStore Connect 到之后筋讨,Apple 也會對安裝包做一些處理,測試安裝包的變化無法對應到真正的下載大小變化的變化摸恍。處理主要包括:
App Slicing 對于不同架構的裁剪悉罕,可執(zhí)行文件只剩下單架構;
Asset.car 中圖片只留下設備需要的特定尺寸和壓縮算法的變體;
__TEXT 段加密;
這也是在不同設備上看到的包大小不同的部分原因惊完。
通過分析可知逢倍,瘦身的途徑主要還是針對可執(zhí)行文件和資源的優(yōu)化籽暇。
三、可執(zhí)行文件優(yōu)化
1、刪除無用類
一般的無用代碼篩查方式可以分為動態(tài)和靜態(tài)兩種方式。靜態(tài)的方式主要是通過代碼掃描栈顷、參與編譯構建過程或者分析最終產(chǎn)物來確認哪些代碼沒有被用到逆日。而動態(tài)的方式主要是靠插樁或者運行時信息來獲取哪些代碼沒有執(zhí)行。
1.1 動態(tài)查找
基于插樁的行級別代碼覆蓋率:
基于 GCOV 或者 LLVM Profile 二進制的插樁方案可以實現(xiàn)在運行時收集插樁數(shù)據(jù)來指導無用代碼的刪除萄凤。但插樁方案局限性也顯而易見室抽,插樁會劣化二進制本身的大小和性能,同時原生的插樁方案是無法過審上線靡努。數(shù)據(jù)收集只能局限于線下坪圾。
基于 Runtime 的輕量級運行時「類覆蓋率」方案:
Objc 的類首次調用類初始化時,+initialize 被執(zhí)行惑朦,系統(tǒng)會自動標記已被調用神年,在 metaClass 中 data 的 flags 字段第 29 位就存著這個這個狀態(tài)⌒朽停可以使用 flags & RW_INITIALIZED 獲取。
1.2 靜態(tài)查找
Mach-O文件中垛耳,__DATA`` __objc_classrefs
中記錄了引用類的地址栅屏,__DATA``__objc_classlist
中記錄了所有類的地址,我們通過otool打印對應的信息堂鲜,然后兩者取差值栈雳,再進行符號化,就得到?jīng)]有被引用的類信息缔莲。
通過
otool -v -s __DATA __objc_classrefs
獲取到引用類(明確用到的)的地址哥纫。通過
otool -v -s __DATA __objc_classlist
獲取所有類的地址。用所有類信息減去引用類的信息痴奏,此時我們可以拿到未使用類的地址信息蛀骇。
通過
nm -nm
命令可以得到地址和對應的類名字。
通過otool -v -s __DATA __objc_classrefs
獲取到引用類的地址读拆。
def classref_pointers(path, binary_file_arch):
ref_pointers = set()
lines = os.popen('/usr/bin/otool -v -s __DATA __objc_classrefs %s' % path).readlines()
for line in lines:
pointers = pointers_from_binary(line, binary_file_arch)
ref_pointers = ref_pointers.union(pointers)
return ref_pointers
通過otool -v -s __DATA __objc_classlist
獲取所有類的地址擅憔。
def classlist_pointers(path, binary_file_arch):
list_pointers = set()
lines = os.popen('/usr/bin/otool -v -s __DATA __objc_classlist %s' % path).readlines()
for line in lines:
pointers = pointers_from_binary(line, binary_file_arch)
list_pointers = list_pointers.union(pointers)
return list_pointers
用所有類信息減去引用類的信息,此時我們可以拿到未使用類的地址信息檐晕。
unref_pointers = classlist_pointers(path, binary_file_arch) - classref_pointers(path, binary_file_arch)
通過nm -nm
命令可以得到地址和對應的類名字暑诸。
def class_symbols(path):
symbols = {}
re_class_name = re.compile('(\w{16}) .* _OBJC_CLASS_\$_(.+)')
lines = os.popen('nm -nm %s' % path).readlines()
for line in lines:
result = re_class_name.findall(line)
if result:
(address, symbol) = result[0]
symbols[address] = symbol
return symbols
得到結果輸出到txt中
由于是靜態(tài)查找,對于動態(tài)生成的類辟灰,比如通過反射生成的類个榕,會被認為沒有被引用,所以查找出列表后芥喇,還需要人工檢查一遍西采。
優(yōu)化結果:刪除無用類110個,收益0.5M乃坤。
2苛让、編譯選項優(yōu)化
2.1 開啟LTO
編譯選項Link-Time Optimization優(yōu)化
蘋果官方介紹沟蔑,開啟LTO后會使在release下的運行速度提升10%,而且包體積會減小狱杰。
Apple uses LTO extensively internally
Typically 10% faster than executables from regular Release builds Multiplies
with Profile Guided Optimization (PGO)
Reduces code size when optimizing for size
但是有個缺點瘦材,debug時的編譯速度慢了很多,而且二次編譯時會全部編譯仿畸,所以我們只是在release模式下開啟了LTO食棕。
2.2 Optimization Level
Optimization Level是指clang采用什么樣的編譯優(yōu)化等級,在Clang的文檔里 clang - Code Generation Options 可以查閱到主要有以下等級:
-O0
Means “no optimization”: this level compiles the fastest and generates the most debuggable code.
-O1
Somewhere between -O0
and -O2
.
-O2
Moderate level of optimization which enables most optimizations.
-O3
Like -O2
, except that it enables optimizations that take longer to perform or that may generate larger code (in an attempt to make the program run faster).
-Ofast
Enables all the optimizations from -O3
along with other aggressive optimizations that may violate strict compliance with language standards.
-Os
Like -O2
with extra optimizations to reduce code size.
-Oz
Like -Os
(and thus -O2
), but reduces code size further.
Xcode默認debug時為-O0
不優(yōu)化错沽,release時為-Os
簿晓。經(jīng)過測試這里如果使用-Oz
會大約減小3M左右的包體積,但是在一些頁面會出現(xiàn)crash, 經(jīng)過排查是一些延遲釋放導致的內(nèi)存問題千埃。出于安全考慮憔儿,目前采用的是-Os
這種優(yōu)化等級。
2.3 符號相關
symbols是指程序中的所有的變量放可、類谒臼、函數(shù)、枚舉耀里、變量和地址映射關系蜈缤,以及一些在調試的時候使用到的用于定位代碼在源碼中的位置的調試符號,符號和斷點定位以及堆棧符號化有很重要的關系冯挎。
2.3.1 Strip Linked Product (STRIP_INSTALLED_PRODUCT
)
If enabled, the linked product of the build will be stripped of symbols when performing deployment postprocessing.
如果設置為yes底哥,打包的時候會將symbols裁剪。
并不是所有的符號都是必須的房官,比如 Debug Map趾徽,所以 Xcode 提供給我們 Strip Linked Product 來去除不需要的符號信息(Strip Style 中選擇的選項相應的符號),去除了符號信息之后我們就只能使用 dSYM 來進行符號化了翰守,所以需要將 Debug Information Format 修改為 DWARF with dSYM file附较。
2.3.2 **Strip Debug Symbols During Copy **(COPY_PHASE_STRIP
)
Specifies whether binary files that are copied during the build, such as in a Copy Bundle Resources or Copy Files build phase, should be stripped of debugging symbols. It does not cause the linked product of a target to be stripped—use Strip Linked Product (STRIP_INSTALLED_PRODUCT) for that.
與 Strip Linked Product 類似,但是這個是將那些拷貝進項目包的三方庫潦俺、資源或者 Extension 的 Debug Symbol 去除掉拒课,同樣也是使用的 strip 命令。這個選項沒有前置條件事示,所以我們只需要在 Release 模式下開啟早像,不然就不能對三方庫進行斷點調試和符號化了。
2.3.3 Symbols Hidden by Default (GCC_SYMBOLS_PRIVATE_EXTERN
)
When enabled, all symbols are declared private extern
unless explicitly marked to be exported using attribute((visibility("default"))) in code. If not enabled, all symbols are exported unless explicitly marked as private extern
.
意思就是設置為yes后肖爵,所有的symbols都會被申明為private extern卢鹦,經(jīng)過測試,確實可以減小包體積。
工程中的設置如下:
target.build_configurations.each do |config|
config.build_settings['COPY_PHASE_STRIP'] = 'YES'
config.build_settings['GCC_SYMBOLS_PRIVATE_EXTERN'] = 'YES'
config.build_settings['STRIP_INSTALLED_PRODUCT'] = 'YES'
編譯選項優(yōu)化結果:收益4.2M
3冀自、__TEXT段遷移
iOS的可執(zhí)行文件就是一個MachO文件揉稚,MachO結構主要分為 Header、Load Commands熬粗、Data三部分搀玖。
Header
包含該二進制文件的一般信息,字節(jié)順序驻呐、架構類型灌诅、加載指令的數(shù)量等。使得可以快速確認一些信息含末,比如當前文件用于32位還是64位猜拾,對應的處理器是什么、文件類型是什么佣盒。Load Commands
是一張包含很多內(nèi)容的表挎袜。 內(nèi)容包括區(qū)域的位置、符號表肥惭、動態(tài)符號表等宋雏。它們描述了Data
在二進制文件和虛擬內(nèi)存中的布局信息,有了這個布局信息就能夠知道Data
在二進制文件中和虛擬內(nèi)存中是怎樣排布的务豺。Data
存儲了實際的內(nèi)容,通常是對象文件中最大的部分嗦明,包含Segement的具體數(shù)據(jù)笼沥,如靜態(tài)C字符串,帶參數(shù)/不帶參數(shù)的OC方法娶牌,帶參數(shù)/不帶參數(shù)的C函數(shù)奔浅。
以下是在MachOView中查看的結構:
Data的結構又可以分為多個Segment,主要有__PAGEZERO
诗良、__TEXT
汹桦、__DATA
、__LINKEDIT
:
__PAGEZERO
是在可執(zhí)行文件有的鉴裹,動態(tài)庫里沒有舞骆。這個段開始地址為0(NULL指針指向的位置),是一個不可讀径荔、不可寫督禽、不可執(zhí)行的空間,能夠在空指針訪問時拋出異常总处。__TEXT
是代碼段狈惫,里面主要是存放代碼的,該段是可讀可執(zhí)行鹦马,但是不可寫胧谈。__DATA
是數(shù)據(jù)段忆肾,里面主要是存放數(shù)據(jù),該段是可讀可寫菱肖,但不可執(zhí)行客冈。__LINKEDIT
段用于存放簽名信息,該段是只可讀蔑滓,不可寫不可執(zhí)行郊酒。
其中的每一個Segment又可以分為一個或多個Section,而__TEXT
是Data中的一個Segment键袱。
__TEXT
段遷移的方式:
一個Mach-O文件構建的構成主要包括 預處理
-> 編譯
-> 匯編
-> 鏈接
等 4 個階段燎窘。
我們通過在 Other Linker Flags
中添加參數(shù)可以在鏈接期移動Section。
-Wl,-rename_section,__TEXT,__cstring,__RODATA,__cstring
-Wl,-segport,__RODATA,rx,rx
其中 -Wl
的作用就是告訴 Xcode 它后面的參數(shù)是添加給 Ld
鏈接器的蹄咖,這些參數(shù)將在鏈接階段生效褐健。
第一行參數(shù)會新創(chuàng)建一個 __RODATA
段,并把 __TEXT,__cstring
移動到 __RODATA,__cstring
澜汤。
第二行參數(shù)是給 __RODATA
賦予可讀和可執(zhí)行權限蚜迅。
我們先來看移動__TEXT,__cstring
前的 Mach-O 文件:
構建完成后再來看一下移動__TEXT,__cstring
后的 Mach-O 文件:
這樣就成功的移動了__TEXT
段中的一些Section。
facebook早期解決__TEXT
段大小限制問題就是使用的這種方式俊抵,具體參考: Analysis of the Facebook.app for iOS
Facebook avoids this limitation by moving some if the __TEXT
sections into the read only __RODATA
segment. Implementing this trick is really simple: you just need to add a linker flag to rename the chosen sections. And it appears you need absolutely nothing at runtime: the renamed sections will be found automatically. This linker flag is described in the ld man page:
-rename_section orgSegment orgSection newSegment newSection
Renames section orgSegment/orgSection to newSegment/newSection.
You could use it to rename the (__TEXT, __cstring)
section to (__RODATA, __cstring)
by simply adding this line into the Other Linker Flags (OTHER_LDFLAGS):
<pre data-language="plain" id="67Kfj" class="ne-codeblock language-plain" style="border: 1px solid rgb(232, 232, 232); border-top-left-radius: 2px; border-top-right-radius: 2px; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; background-color: rgb(249, 249, 249); padding: 16px; font-size: 13px; color: rgb(89, 89, 89);">-Wl,-rename_section,__TEXT,__cstring,__RODATA,__cstring</pre>
今日頭條在減小下載大小時也采用了這種方式谁不,
通過在Other Linker Flags中添加下面的參數(shù),就可以達到這樣的目的
這里的作用就是對__TEXT
段中的section移動到其他section徽诲,然后賦予讀權限和可執(zhí)行權限刹帕。
那么__TEXT
段遷移為什么會減小下載大小呢?
原因就是App在上傳到App Store Connect后谎替,蘋果會對其進行加密偷溺,然后壓縮成ipa。加密對可執(zhí)行文件本身的大小幾乎沒有影響钱贯,但是卻大大影響了壓縮效率挫掏。而__TEXT
段又是加密段中最主要的一部分,通過減小__TEXT
段就可以減小加密范圍秩命,所以就可以將__TEXT
段中的一些Section遷移到其它Segment中尉共。
優(yōu)化結果:安裝大小減小0.2M,下載大小減小25M弃锐。
4爸邢、三方庫相關
1、推動直播SDK使用精簡版拿愧,由于直播場景不需要實時音視頻杠河、超級播放器SDK以及 AI 特效組件的能力,所以修改了直播SDK。為了防止更換SDK出現(xiàn)問題券敌,進行了兩個版本的灰度觀察唾戚,經(jīng)過充分測試無誤,才確定更換待诅。
2叹坦、推動一些SDK的刪除。刪除了一些可以被取代的SDK卑雁。
優(yōu)化結果:收益7M募书。
四、資源優(yōu)化
資源優(yōu)化主要是對圖片資源和其他一些json测蹲、音頻莹捡、視頻等資源的優(yōu)化。
1扣甲、PNG圖片壓縮
png壓縮主要對比了兩種方案:
TinyPNG
有損壓縮篮赢,主要是使用Quantization的技術,通過合并圖片中相似的顏色琉挖,通過將 24 位的 PNG 圖片壓縮成小得多的 8 位色值的圖片启泣,并且去掉了圖片中不必要的 metadata,這種方式幾乎能完美支持原圖片的透明度示辈。
網(wǎng)站: https://tinypng.com/
ImageOptim
無損壓縮寥茫,圖片文件中往往包含一些注釋、顏色 Profile 等多余信息矾麻,移除后圖像質量不變纱耻,體積更小載入更快。ImageOptim 以此方式壓縮圖片射富,先分析圖片,找到最優(yōu)壓縮參數(shù)粥帚,去除無關信息減小體積胰耗。
網(wǎng)站:https://imageoptim.com/mac
經(jīng)過壓縮測試,發(fā)現(xiàn)TinyPNG壓縮效果遠好于ImageOptim芒涡,TinyPNG壓縮比約為65%柴灯,ImageOptim壓縮比約為30%,并且肉眼看起來無差異费尽。
使用過程中發(fā)現(xiàn)赠群,一些png圖片雖然壓縮后變小了,但是打包后變化并不明顯旱幼,有些甚至變大了查描。通過分析ipa中的png圖片以及查閱資料了解到,由于蘋果本身也會對png圖片進行壓縮,這個壓縮過程是為了加快對圖片的處理速度冬三,將其轉換為更方便處理的格式--CgBI格式匀油。并且修改了存放實際的圖像數(shù)據(jù)的IDAT數(shù)據(jù)塊,改變了決定IDAT數(shù)據(jù)塊大小的Filter方式勾笆、zlib的壓縮方式敌蚜。因為CgBI的IDAT是BGRA格式的,所以不管之前的IDAT是否有Alpha通道窝爪,在處理的時候弛车,都會增加alpha通道,其次就是因為每一行數(shù)據(jù)的filter不同蒲每,蘋果處理的時候纷跛,默認每一行都使用相同的filter,而原始文件則可以通過更好的算法啃勉,對不同的數(shù)據(jù)行使用不同的filter忽舟,為后面的數(shù)據(jù)壓縮提供更容易壓縮的數(shù)據(jù)。因此蘋果對于png的優(yōu)化可能會導致部分png圖片變大的情況淮阐。
所以對于部分壓縮后的png圖片叮阅,我們也會采用轉為WebpP的方式進行進一步處理。
優(yōu)化結果:收益5M
2泣特、PNG圖片轉為WebP圖片
相較于PNG格式, WebP具有更加優(yōu)秀的圖像數(shù)據(jù)壓縮算法浩姥,能帶來更小的圖片體積。所以會對一些較大的圖片轉換為WebP圖片状您。
壓縮采用的是: cwebp
-- Compress an image file to a WebP file
安裝方式:brew install webp
使用方式:
cwebp [options] -q quality input.png -o output.webp
其中option可選:-loss(有損壓縮勒叠,默認),-lossless(無損壓縮)
-q:質量指數(shù)(壓縮率)膏孟,有損壓縮有效眯分,無損壓縮忽略
input.png:待轉換圖片
-o:輸入圖片名稱格式
#png 轉換為webp
toWebp() {
filePath=echo $1 |sed 's/ /\ /g'
fileName={fileName##*/}
fileName=echo $fileName|sed 's/ /_/g'
fileName=${fileName%.*}
# 靜默模式 轉換時將不會打印轉換日志
if [[ -e $LOCAL_CWEBP_PATH ]]; then
cwebp -quiet "$filePath" -o $newFilePath$fileName.webp
else $basedir/bin/cwebp -quiet "$filePath" -o $newFilePath$fileName.webp
fi
echo $filePath
printResult $? "${filePath##*/} ? $newFilePath$fileName.webp"
}
在轉換時,本以為腳本將png圖片轉為webp圖片柒桑,然后hook圖片加載方式就可以讀取webp圖片了弊决,但是發(fā)現(xiàn)png圖片是由imageset管理的,代碼中使用的圖片名稱和png圖片名稱可能不一致魁淳。
解決方法:腳本轉換成webp的時候飘诗,不能直接使用png圖片名稱,而是要使用管理png的imageset名稱界逛。
解決完昆稿,run起來后又發(fā)現(xiàn)了另外一個問題,圖片展示都放大了息拜,經(jīng)過排查溉潭,發(fā)現(xiàn)png有1x净响、2x、3x三種岛抄,一個60x60像素的3x圖片生成的UIImage對象scale為3别惦,size為20x20,但是轉為webp再生成UIImage之后夫椭,UIImage的scale為1掸掸,size為60,所以顯示時圖片變大了蹭秋。還好在SDImageCoder中找到了一個修改scale參數(shù)的轉換方法扰付。
/**
Decode the image data to image.
@note This protocol may supports decode animated image frames. You can use `+[SDImageCoderHelper animatedImageWithFrames:]` to produce an animated image with frames.
@param data The image data to be decoded
@param options A dictionary containing any decoding options. Pass @{SDImageCoderDecodeScaleFactor: @(1.0)} to specify scale factor for image. Pass @{SDImageCoderDecodeFirstFrameOnly: @(YES)} to decode the first frame only.
@return The decoded image from data
*/
- (nullable UIImage *)decodedImageWithData:(nullable NSData *)data
options:(nullable SDImageCoderOptions *)options;
但是另外一個問題又出現(xiàn)了,本想著統(tǒng)一由3x的png圖片轉為webp仁讨,然后scale參數(shù)傳3羽莺,但是由于以前圖片管理不規(guī)范,導致png圖片有些只有1x圖洞豁,有些只有2x或者3x圖盐固,所以這里還需要根據(jù)webp是哪種png圖片轉換來的,傳對應的scale參數(shù)丈挟。
然后后面測試時又發(fā)現(xiàn)部分圖片加載不出來刁卜,排查發(fā)現(xiàn)這些圖片是在xib中讀取的,而xib讀取png的方式并不通過imageNamed
方法曙咽,然后首先第一個想法自然是hook xib讀取png的方法蛔趴,但是蘋果并沒有暴露給我們xib加載png的方法。也有一些資料說可以通過hook UINibDecoder的decodeObjectFotKey方法例朱,但是覺得并不十分嚴謹孝情,所以xib中的使用的png, 我采用了另外一種方式:在代碼中將控件使用imageNamed
方法再讀取一遍圖片。
還有一點需要注意的是洒嗤,一些可以支持區(qū)域拉伸的png圖轉為webp后拉升是會變形的箫荡。這部分圖是不適合轉為webp的。
優(yōu)化結果:收益6M
3渔隶、修改組件庫中圖片管理方式
Asset Catalog羔挡,是Xcode提供的一項圖片資源管理方式。每個Asset表示一個圖片資源派撕,但是可以對應一張或者多張PNG圖婉弹,比如可以提供@1x
, @2x
, @3x
多張尺寸的圖進行適配睬魂;
Asset Catalog中的圖片终吼,在編譯時會被壓縮,然后在App運行時氯哮,可以通過API動態(tài)根據(jù)設備scale factor來選擇對應的真實的圖片渲染际跪,使用Asset Catalog管理的圖片會在ipa包中生成一個Assets.car文件商佛。
App Thing,是蘋果平臺上的一個用于優(yōu)化App包下載資源大小的方案姆打。在App包提交上傳到App Store后良姆,蘋果后臺服務器,會對不同的設備幔戏,根據(jù)設備的scale factor玛追,重新把App包進行精簡,這樣不同設備從App Store下載需要的容量不同闲延,3x設備不需要同時下載1x和2x的圖痊剖。
但是,這套機制直接基于Asset Catalog垒玲,也就是說陆馁,只有在Asset Catalog中引入的圖片,才能享受到App Thinning合愈。直接拷貝到App Bundle中的散落圖片叮贩,所有設備還是都會全部下載。
因此盡量提升Asset Catalog利用率佛析,是一個很大的包大小優(yōu)化點益老。
所以在使用cocoapods進行組件庫管理時,組件庫中的PNG圖片也都使用Asset Catalog來管理说莫。
除此之外還有一個資源引入方式的不同:pod中的資源引入方式有兩種杨箭,resource_bundles和resources。
使用resources储狭,會在主bundle中導入互婿。這種方式讀取圖片不需要修改讀取方式。
s.resources = ['ResourcesTest/Assets/*.xcassets']
使用resource_bundles辽狈,會在主bundle中生成一個自定義的bundle慈参,bundle中存放著資源。讀取資源時需要到對應bundle下讀取刮萌。這種方式可以避免命名沖突驮配。
s.resource_bundles = {
'ResourcesBubdlesTest' => ['ResourcesBubdlesTest/Assets/*.xcassets']
}
在修改的過程中,也有一些意外收獲着茸,發(fā)現(xiàn)項目中部分組件庫引入資源方式存在問題壮锻,同時指定了resource_bundles和resources兩種方式,這樣會導致圖片既存在main bundle中又存在resource_bundles生成的bundle中涮阔。
這里推薦使用resource_bundles + Asset Catalog的方式來管理組件庫中的PNG圖片猜绣。
優(yōu)化結果:收益4.5M
4、刪除無用PNG圖片
通過工具篩查:LSUnusedResources
優(yōu)化結果:刪除圖片56張敬特,收益1.2M
5掰邢、壓縮文本文件
玩物得志APP中有一些Lottie動畫的json文件存放在本地牺陶,通過對這些文件的打包壓縮也取得了一些優(yōu)化效果。
將本地中較大的json文件放到一起壓縮成zip辣之;
啟動時在異步線程解壓zip掰伸,存放到沙盒中;
運行時從沙盒中讀取json;
后續(xù)可以對更多的資源類型采用這種方式怀估,比如音頻狮鸭、視頻。
優(yōu)化結果:收益1.2M
五多搀、包大小監(jiān)控
為了控制增量怕篷,我們也對每個版本做了包大小的監(jiān)控。
針對可執(zhí)行文件的變化酗昼,我們采用的是LinkMap來分析每個組件的大小變化并進行記錄廊谓。
針對資源的變化,我們也會從每個版本的ipa包中分析出資源大小的變化并記錄麻削。
以后蒸痹,我們計劃對增量進行卡口,結合盤古打包平臺進行包體積自動分析呛哟,在每次分支被合并到主分支之前叠荠,就能體現(xiàn)出增量大小,這樣可以使得開發(fā)對自己開發(fā)的代碼有更直觀的感受扫责,加強開發(fā)在日常編碼中的瘦身意識榛鼎,有意識的去對資源、代碼進行清理鳖孤。爭取做到包體積的零增量者娱。
六、效果
以上所有方案都在玩物得志APP中進行了實踐和落地苏揣,經(jīng)過一系列優(yōu)化黄鳍,玩物得志APP包體積整體收益如下:
下載大小由136.2M降為78.6M,減小57.6M
安裝大小由174.5M降為140M平匈,減小34.5M
下載大小最直觀的體現(xiàn)就是下載時間的長短框沟,以下對比了3.5.0和3.6.7兩個版本的下載安裝時長:
優(yōu)化前:下載安裝時長為64s
優(yōu)化后:下載安裝時長為43s
下載時長縮短32.8%
以下是記錄的優(yōu)化以來各個版本下載大小與安裝大小的變化: