前言
如今裹刮,日子是越來(lái)越好了音榜,大多數(shù)的同學(xué)有時(shí)還是管不住自己的嘴,一不留神把自己吃成了“小胖子”必指;軟件也如此囊咏,隨著科技的發(fā)展,手機(jī)硬件配置的提高塔橡,手機(jī)App的功能也越來(lái)越多梅割,經(jīng)過(guò)1年左右的開(kāi)發(fā)疊代,“百度高考iOS”從最初1.0版的10MB左右葛家,不知不覺(jué)户辞,安裝程序已43MB+了,唉癞谒,“減肥”迫在眉睫暗琢恰!5狻K浴!
現(xiàn)實(shí)問(wèn)題
說(shuō)回到我們的百度高考iOS桌吃,我們?cè)缦仁?周左右一版朱沃,差不多是2周開(kāi)發(fā),1周測(cè)試茅诱;隨著項(xiàng)目的成長(zhǎng)逗物,團(tuán)隊(duì)的逐步擴(kuò)大,需求也是一波波的襲來(lái)瑟俭,我們也逐漸適應(yīng)了2周一版的疊代周期翎卓,到現(xiàn)在為止,已發(fā)布16個(gè)版本摆寄∈П回想這一年的開(kāi)發(fā),功能是加加減減微饥,界面是修修改改锐帜,但引入的資源文件幾乎是只加不減,而這些資源差不多分為這么幾類:圖片素材畜号、配置文件、每日美文允瞧、H5模板简软、第三方類庫(kù)和音頻文件蛮拔,不用想,圖片素材當(dāng)然是占用空間最大的一類痹升,“減肥”也自然先那它“下手”了建炫,但具體又有哪些方面可以下手呢?
我的猜想
結(jié)合現(xiàn)實(shí)的問(wèn)題疼蛾,給ipa“減肥”肛跌,我覺(jué)得大體可以從如下方面著手:
刪除項(xiàng)目中無(wú)用的文件,如被棄用的類察郁、圖片等衍慎,猜測(cè)差不多能減掉個(gè)2~3MB
-
其次當(dāng)然就是圖片了,可以使用圖片壓縮工具皮钠,通過(guò)無(wú)損或有損壓縮的方式減小圖片大小稳捆,假設(shè)我們的項(xiàng)目中有30M的圖片,然后將它們有損壓縮到80%的質(zhì)量麦轰,那么就可以減掉6MB左右乔夯。(PS:根據(jù)大煒哥的經(jīng)驗(yàn),這是最有效的給ipa“減肥”方法了)
說(shuō)到這里款侵,我想到了另一個(gè)問(wèn)題:都知道末荐,圖片的導(dǎo)入方式有如下幾種:
-
加入到Assets.xcassets中
- 只支持png格式的圖片
- 圖片只支持
[UIImage imageNamed]
的方式實(shí)例化,但是不能從Bundle中加載 - 在編譯時(shí)新锈,Images.xcassets中的所有文件會(huì)被打包為Assets.car的文件
-
CreateGroup
- 黃色文件夾圖標(biāo)甲脏;Xcode中分文件夾,Bundle中所有所在都在同一個(gè)文件夾下壕鹉,因此剃幌,不能出現(xiàn)文件重名的情況
- 可以直接使用
[NSBundle mainBundle]
作為資源路徑,效率高晾浴! - 可以使用
[UIImage imageNamed:]
加載圖像
-
CreateFolderRefences
- 藍(lán)色文件夾负乡;Xcode中分文件夾,Bundle中同樣分文件夾脊凰,因此抖棘,可以出現(xiàn)文件重名的情況
- 需要在
[NSBundle mainBundle]
的基礎(chǔ)上拼接實(shí)際的路徑,效率較差 - 不能使用
[UIImage imageNamed:]
加載圖
PDFs矢量圖(Xcode6+)
-
Bundle(包)中的圖片素材
那這不同的導(dǎo)入方式狸涌,會(huì)對(duì)打出的包的大小有影響么切省?
-
以上2個(gè)角度是我能想到的(也是最好想到的)“減肥”方法了,除此之外帕胆,夢(mèng)凱還給我提過(guò)一個(gè)技術(shù)名詞——
BitCode
——?jiǎng)偮?tīng)到這個(gè)詞的時(shí)候我還懵逼了一會(huì)兒朝捆。大概的意思是說(shuō):之前打包,可以運(yùn)行在各個(gè)不同型號(hào)的iOS設(shè)備上懒豹,是因?yàn)樵诖虬臅r(shí)候芙盘,蘋果幫我們把a(bǔ)pp在各型號(hào)設(shè)備上運(yùn)行所需要的“東西”一并全部打到包里了驯用。假設(shè)我們?cè)诖虬臅r(shí)候,只把要運(yùn)行的設(shè)備所需的“東西”打到包里儒老,而不需要其他型號(hào)運(yùn)行所需要的“東西”蝴乔,這樣不就達(dá)到減小ipa大小的目的了么?BitCode就是來(lái)完成這個(gè)任務(wù)的中間件驮樊。
那么問(wèn)題來(lái)了薇正,BitCode真的有這么厲害么?它到底是怎樣做到的呢囚衔?接入它麻煩么挖腰?。佳魔。曙聂。
給ipa減個(gè)肥
如何優(yōu)雅的清理掉那些棄用的文件
一個(gè)項(xiàng)目開(kāi)發(fā)得越久,添加的功能模塊也就越多鞠鲜,相應(yīng)地宁脊,也會(huì)慢慢引入大量圖片等資源。但是贤姆,在移除一些不再使用的模塊的時(shí)候榆苞,開(kāi)發(fā)者往往會(huì)忘記把該模塊所對(duì)應(yīng)的圖片資源一起刪除,因?yàn)樵创a和資源是分離的霞捡。長(zhǎng)久以來(lái)坐漏,項(xiàng)目中就會(huì)存在大量沒(méi)被使用的資源文件。
具體方法無(wú)非是碧信,一個(gè)一個(gè)地復(fù)制資源文件名赊琳,然后在 XCode 中全局查找該字符串,如果結(jié)果為 0砰碴,那么這個(gè)資源很可能就沒(méi)有被使用躏筏。為什么說(shuō)很可能?因?yàn)樵诖a中呈枉,有可能通過(guò)字符串拼接的方式使用了這個(gè)資源趁尼,而這種情況是沒(méi)辦法通過(guò)字符串匹配查找出來(lái)的。
于是猖辫,我們需要這么一款工具:能夠迅速找出工程中所有沒(méi)被使用的資源文件酥泞。
腳本
詳見(jiàn)鏈接
#!/bin/sh
PROJ=`find . -name '*.xib' -o -name '*.[mh]'`
for png in `find . -name '*.png'`
do
name=`basename $png`
if ! grep -qhs "$name" "$PROJ"; then
echo "$png is not referenced"
fi
done
缺點(diǎn):不夠智能,不夠通用啃憎,速度太慢芝囤,結(jié)果不正確。
Unused
Unused:
A Mac app for checking Xcode projects for unused resources
Unused 對(duì)腳本的調(diào)用做了封裝,通過(guò)界面可以配置一定的信息凡人,然后比較清晰的輸入結(jié)果名党。
缺點(diǎn):實(shí)際上,Unused 的內(nèi)部還是調(diào)用了方案 1 的腳本挠轴,所以方案 1 的缺點(diǎn)也就是方案 2 的缺點(diǎn)。
LSUnusedResources與改進(jìn)
提高匹配速度
LSUnusedResources 很大程度上受 Unused 的影響耳幢,比如界面岸晦、交互,以及部分代碼睛藻。但是启上,本工具在核心代碼上做了優(yōu)化,使其在搜索的速度店印、結(jié)果的正確上都有了很大的提高冈在。
核心步驟,簡(jiǎn)述如下:
查找:選定的目錄下的所有資源文件按摘。這一步與上述方案1區(qū)別不大包券,都是調(diào)用 find 命令查找指定后綴名的文件。
匹配:與上述方案不同炫贤,我不是對(duì)每個(gè)資源文件名都做一次全文搜索匹配溅固,因?yàn)榧尤腠?xiàng)目的資源太多,這里會(huì)導(dǎo)致性能快速下降兰珍。而我只是針對(duì)源碼侍郭、Xib、Storyboard 和 plist 等文件掠河,先全文搜索其中可能是引用了資源的字符串亮元,然后用資源名和字符串做匹配。
3.2 優(yōu)化匹配結(jié)果
Unused 會(huì)把大量實(shí)際上有使用的資源唠摹,當(dāng)做未使用的資源輸出爆捞。LSUnusedResources 則不會(huì)出現(xiàn)這樣的問(wèn)題,并且使得結(jié)果更加優(yōu)化跃闹。
舉例說(shuō)明:
你在工程中添加了下面資源:
icon_tag_0.png
icon_tag_1.png
icon_tag_2.png
icon_tag_3.png
然后用字符串拼接的方式在代碼中引用:
NSInteger index = random() % 4;
UIImage *img = [UIImage imageNamed:[NSString stringWithFormat:@"icon_tag_%d", index]];
icon_tag_x.png 是不應(yīng)該被當(dāng)做未使用的資源的嵌削,只是以一種比較隱晦的方式間接引用了,所以不應(yīng)該出現(xiàn)在結(jié)果列表中望艺。
使用方法
點(diǎn)擊 Browse.. 選擇一個(gè)文件夾苛秕;
點(diǎn)擊 Search 開(kāi)始搜索;
等待片刻即可看到結(jié)果找默。
下載安裝
下載: LSUnusedResources.app.zip或者使用 XCode 編譯運(yùn)行項(xiàng)目代碼艇劫。
正確導(dǎo)入圖片的姿勢(shì)
經(jīng)過(guò)測(cè)試得知:CreateGroup、CreateFolderRefences兩種方式打出來(lái)的包惩激,圖片都會(huì)直接放在.app文件中店煞,所以打包前后蟹演,圖片的大小不會(huì)改變
而加入到Assets.xcassets中的方法則不同,打包后顷蟀,在.app中會(huì)生成Assets.car文件來(lái)存儲(chǔ)Assets.xcassets中的圖片酒请,并且文件大小方面也大大降低
打包前Assets.xcassets文件夾 | 打包后的Assets.car文件夾 | |
---|---|---|
測(cè)試1 | 32.7MB | 16.3MB |
測(cè)試1 | 33.5MB | 26.1MB |
所以,使用Assets.xcassets來(lái)管理圖片也可以達(dá)到ipa瘦身的效果~~
壓壓更健康 - imageoptim
imageoptim是一款基于Mac的圖像“瘦身”軟件鸣个,內(nèi)置有6種壓縮算法羞反,通過(guò)刪除圖片部分無(wú)用的EXIF等信息來(lái)減小PNG、JPEG和GIF圖片的大小囤萤。ImageOptim合并了OptiPNG昼窗、PNGCrush、AdvanceComp涛舍、PNGOUT澄惊、Jpegoptim+Jpegtran和Gifsicle等幾個(gè)工具,旨在為設(shè)計(jì)師提供最好的優(yōu)化效果富雅。在最新發(fā)布的1.4.4版本中掸驱,ImageOptim改進(jìn)了文件在文件列表中的拖拽、復(fù)制吹榴、粘貼功能亭敢。 下載地址
------(分割線)-------
話說(shuō)項(xiàng)目中10M的圖片,按照默認(rèn)的80%壓縮图筹,也小了4M(見(jiàn)上圖)帅刀,可是實(shí)際打包出來(lái),為什么ipa沒(méi)有小呢远剩?扣溺?
BitCode
Ta是誰(shuí)
我們先來(lái)看下官方文檔:在App Distribution Guide – App Thinning (iOS, watchOS)一節(jié)中,找到了下面這樣一個(gè)定義:
Bitcode is an intermediate representation of a compiled program. Apps
you upload to iTunes Connect that contain bitcode will be compiled and
linked on the App Store. Including bitcode will allow Apple to
re-optimize your app binary in the future without the need to submit a
new version of your app to the store.
說(shuō)的是bitcode是被編譯程序的一種中間形式的代碼瓜晤。包含bitcode配置的程序?qū)?huì)在App store上被編譯和鏈接锥余。bitcode允許蘋果在后期重新優(yōu)化我們程序的二進(jìn)制文件,而不需要我們重新提交一個(gè)新的版本到App store上痢掠。
亮個(gè)劍
真是不明覺(jué)厲啊驱犹,繼續(xù)看,在What’s New in Xcode-New Features in Xcode 7中足画,還有一段如下的描述
Bitcode. When you archive for submission to the App Store, Xcode will
compile your app into an intermediate representation. The App Store
will then compile the bitcode down into the 64 or 32 bit executables
as necessary.
當(dāng)我們提交程序到App store上時(shí)雄驹,Xcode會(huì)將程序編譯為一個(gè)中間表現(xiàn)形式(bitcode)。然后App store會(huì)再將這個(gè)botcode編譯為可執(zhí)行的64位或32位程序淹辞。
再看看這兩段描述都是放在App Thinning(App瘦身)一節(jié)中医舆,可以看出其與包的優(yōu)化有關(guān)了。喵大(@onevcat)在其博客開(kāi)發(fā)者所需要知道的 iOS 9 SDK 新特性中也描述了iOS 9中蘋果在App瘦身中所做的一些改進(jìn),大家可以轉(zhuǎn)場(chǎng)到那去研讀一下蔬将。
另外可以參看Xcode 7 Bitcode的工作流程及安全性評(píng)估了解更多爷速。
請(qǐng)賜教
實(shí)際上,在Xcode 7中霞怀,我們新建一個(gè)iOS程序時(shí)惫东,bitcode選項(xiàng)默認(rèn)是設(shè)置為YES的。我們可以在”Build Settings”->”Enable Bitcode”選項(xiàng)中看到這個(gè)設(shè)置里烦。
不過(guò)凿蒜,我們現(xiàn)在需要考慮的是三個(gè)平臺(tái):iOS,Mac OS胁黑,watchOS。
對(duì)于iOS州泊,bitcode是可選的丧蘸;對(duì)于watchOS,bitcode是必須的遥皂;而Mac OS是不支持bitcode力喷。
如果我們開(kāi)啟了bitcode,在提交包時(shí)演训,下面這個(gè)界面也會(huì)有個(gè)bitcode選項(xiàng):
所以弟孟,如果我們的工程需要支持bitcode,則必要要求所有引入的第三方庫(kù)都支持bitcode样悟。
他山之石以攻玉
除了上面提到的“減肥”方法之外拂募,還有其他的方案或思路么?
編譯選項(xiàng)
編譯器優(yōu)化級(jí)別
Build Settings->Optimization Level有幾個(gè)編譯優(yōu)化選項(xiàng)窟她,release版應(yīng)該選擇Fastest, Smalllest陈症,這個(gè)選項(xiàng)會(huì)開(kāi)啟那些不增加代碼大小的全部?jī)?yōu)化,并讓可執(zhí)行文件盡可能小震糖。去除符號(hào)信息
Strip Debug Symbols During Copy 和 Symbols Hidden by Default 在release版本應(yīng)該設(shè)為yes录肯,可以去除不必要的調(diào)試符號(hào)。Symbols Hidden by Default會(huì)把所有符號(hào)都定義成”private extern”吊说,具體意思和作用我還不清楚论咏,有待研究,但設(shè)了后會(huì)減小體積颁井。這些選項(xiàng)目前都是XCode默認(rèn)選項(xiàng)厅贪,但舊版XCode生成的項(xiàng)目可能不是,可以檢查一下蚤蔓。
第三方庫(kù)
項(xiàng)目里會(huì)引入很多第三方靜態(tài)庫(kù)卦溢,如果能知道這些第三方庫(kù)在可執(zhí)行文件里占用的大小,就可以評(píng)估是否值得去找替代方案去掉這個(gè)第三方庫(kù)。
ARC->MRC
有人提出用ARC寫(xiě)的代碼編譯出來(lái)的可執(zhí)行文件是會(huì)比用MRC大的单寂,原因大致是ARC代碼會(huì)在某些情況多出一些retain和release的指令贬芥,例如調(diào)用一個(gè)方法,它返回的對(duì)象會(huì)被retain宣决,退出作用域后會(huì)被release蘸劈,MRC就不需要,匯編指令變多尊沸,機(jī)器碼變多威沫,可執(zhí)行文件就變大了。還有其他細(xì)節(jié)實(shí)現(xiàn)的區(qū)別洼专,先不糾結(jié)了棒掠。
那用ARC究竟會(huì)增大多少體積?我覺(jué)得從匯編指令的增多減少去算是很難算準(zhǔn)確的屁商,這東西涉及細(xì)節(jié)太多烟很,還是得從統(tǒng)計(jì)的角度計(jì)算。做了幾個(gè)對(duì)比試驗(yàn)蜡镶,統(tǒng)計(jì)了幾個(gè)同時(shí)支持ARC/MRC的開(kāi)源項(xiàng)目在開(kāi)啟/關(guān)閉ARC的情況下__TEXT代碼段的大小對(duì)比雾袱。只對(duì)比__TEXT代碼段是因?yàn)椋?/p>
ARC對(duì)可執(zhí)行文件大小的影響幾乎都是在代碼段
可執(zhí)行文件會(huì)進(jìn)行某種對(duì)齊,例如有些段在不足32K的時(shí)候填充0直到對(duì)齊32K官还,若用可執(zhí)行文件大小對(duì)比結(jié)果可能是對(duì)齊后的芹橡,不準(zhǔn)確。
對(duì)比實(shí)驗(yàn)數(shù)據(jù)望伦,結(jié)論是ARC大概會(huì)使代碼段增加10%的size林说,考慮代碼段占可執(zhí)行文件大約有80%,估計(jì)對(duì)整個(gè)可執(zhí)行文件的影響會(huì)是8%屡谐。
可以評(píng)估一下8%的體積下降是不是值得把項(xiàng)目里某些模塊改成MRC述么,這樣程序的維護(hù)成本上升了,一般不到特殊情況不建議這么做愕掏。
類/方法名長(zhǎng)度
觀察linkmap可以發(fā)現(xiàn)每個(gè)類和方法名都在__cstring段里都存了相應(yīng)的字符串值度秘,所以類和方法名的長(zhǎng)短也是對(duì)可執(zhí)行文件大小是有影響的,原因還是object-c的動(dòng)態(tài)特性饵撑,因?yàn)樾枰ㄟ^(guò)類/方法名反射找到這個(gè)類/方法進(jìn)行調(diào)用剑梳,object-c對(duì)象模型會(huì)把類名,方法名列表都保存下來(lái)滑潘。
可以考慮在編譯前把所有類和方法名進(jìn)行混淆垢乙,把長(zhǎng)名字替換成短名字,這樣做的好處除了縮小體積外语卤,還對(duì)安全性有很大提升追逮,別人拿到可執(zhí)行文件對(duì)它c(diǎn)lass-dump出來(lái)的結(jié)果都是混淆后的類和方法名酪刀,就無(wú)法從類和方法名中猜出某個(gè)方法是做什么的,就難以掛鉤子進(jìn)行hack钮孵。不過(guò)這樣有個(gè)缺點(diǎn)就是crash堆棧反解出來(lái)的堆棧方法名會(huì)是混淆后的骂倘,需要再加一層混淆->原名的轉(zhuǎn)換,實(shí)現(xiàn)和使用成本有點(diǎn)高巴席。
實(shí)際上這部分占用的長(zhǎng)度比較小历涝,中型項(xiàng)目也就幾百K,對(duì)安全性要求高的情況可以試試漾唉。
冗余字符串
代碼上定義的所有靜態(tài)字符串都會(huì)記錄在在可執(zhí)行文件的__cstring段荧库,如果項(xiàng)目里L(fēng)og非常多,這個(gè)空間占用也是可觀的赵刑,也有幾百K的大小分衫,可以考慮清理所有冗余的字符串。另外如果有特別長(zhǎng)的字符串般此,建議抽離保存成靜態(tài)文件丐箩,因?yàn)锳ppStore對(duì)可執(zhí)行文件加密導(dǎo)致壓縮率低,特別長(zhǎng)的字符串抽離成靜態(tài)資源文件后壓縮率會(huì)比在可執(zhí)行文件里高很多恤煞。
參考
- 查找XCode工程中沒(méi)被使用的圖片資源
- 理解Bitcode:一種中間代碼
- iOS中Bitcode的介紹及配置
- ios打包ipa的四種實(shí)用方法(.app轉(zhuǎn).ipa)
- iOS APP可執(zhí)行文件的組成
- iOS可執(zhí)行文件瘦身方法
- 查找XCode工程中沒(méi)被使用的圖片資源
- 在xcode6中使用矢量圖(iPhone6置配UI)
- Xcode 6 可以使用 PDF 格式的矢量圖作為圖片資源,自動(dòng)適配施籍?
- USING VECTOR IMAGES IN XCODE 6
- 深入理解iOS開(kāi)發(fā)中的BitCode功能
- iOS開(kāi)發(fā)者必備:六大圖片居扒、圖標(biāo)處理類工具
- iOS開(kāi)發(fā)APP如何讓iOS可執(zhí)行文件進(jìn)行瘦身!