iOS-APP瘦身

屏幕快照 2016-07-15 上午10.47.09.png

資源優(yōu)化

1. 去除無用資源

一般都是版本迭代過程中存在的圖片資源。
可以借助三方工具來解決: Unused-master LSUnusedResources

通過Unused-master可以查找到一些工程中沒有用到的圖片,但這個(gè)工具并不是百分百正確,在圖片刪除的時(shí)候要慎重看一下。在掃描結(jié)果中,查找到了一些命名錯(cuò)誤的圖片,例如:xxx2x.png、xxx.png這樣的圖片命名方式會(huì)影響APP性能

D3BEE3F9-029C-41E3-B704-772A3EA0EB0A.png
2. 資源壓縮
2.1. 圖片無損壓縮

這個(gè)主要是利用三方工具ImageOptim來實(shí)現(xiàn):

屏幕快照 2016-07-04 下午1.54.17.png

ImageOptim Mac版是一款非常簡(jiǎn)單的圖片大小優(yōu)化工具。只要拖動(dòng)圖片到軟件界面就可以自動(dòng)把圖片的大小進(jìn)行優(yōu)化。ImageOptim Mac版對(duì)于開發(fā)人員和設(shè)計(jì)人員一定還有用處,如文件的EXIF標(biāo)簽和顏色配置文件等,達(dá)到優(yōu)化減小占用磁盤空間。

圖片文件中往往包含一些注釋、顏色 Profile 等多余信息,移除后圖像質(zhì)量不變,體積更小載入更快。ImageOptim 以此方式壓縮圖片,先分析圖片,找到最優(yōu)壓縮參數(shù),去除無關(guān)信息減小體積,實(shí)行無損壓縮。

62B4D991-1827-491C-9FC9-C9C94C6C5567.png

關(guān)于圖片資源補(bǔ)充:

1.能用縮略圖的用縮略圖、單一色彩可以用純色代替
2.盡量使用8-bit圖片使用8-bit的PNG圖片,比32-bit的圖片能減少4倍的壓縮率。 由于8-bit的圖片支持最多256種不同的顏色,所以8-bit的圖片一般只應(yīng)該用于一小 部分的顏色圖片。例如灰度圖片最好使用8-bit。

2.2. 音頻壓縮

參考WWDC中的Audio Development for Games,里面介紹了如何有效的處理音頻。常規(guī)來說,我們要使用AAC或MP3來壓縮音頻,并且可以嘗試降低一下音頻的比特率。有時(shí)候44.1khz的采樣是沒有必要的,稍微低一點(diǎn)的比特率也不會(huì)降低音頻的質(zhì)量。

2.3. 查找文件大小

通過查找工程中文件的大小,可以找到一些較大的圖片和三方庫,視情況壓縮或替換這個(gè)主要是用命令行,cd到工程目錄 然后輸入find . -size +100 就會(huì)遍歷出工程目錄下大于100k的文件,如果是+1000 就是大于1000k的,以此類推...
例如:Robin:SimpleFinance zhaojijin$ find . -size +1000

0F7BB855-3C4F-424E-B1C5-788B53FC107A.png

這個(gè)方法掃描到的文件并不是完全正確,如上圖,這個(gè)結(jié)果中changeCard_lostCardStepOnePic@3x.png finder中實(shí)際大小是854k,但這并不影響使用,大體還是對(duì)的

3.不常用資源換為下載

比如自定義字體,可以在APP第一次啟動(dòng)后動(dòng)態(tài)獲取

編譯優(yōu)化

1. 去除符號(hào)信息

Strip Debug Symbols During Copy 和 Symbols Hidden by Default 在release版本應(yīng)該設(shè)為yes,可以去除不必要的調(diào)試符號(hào),當(dāng)然xcode默認(rèn)就是yes

2. 開啟編譯優(yōu)化

Build Settings->Optimization Level有幾個(gè)編譯優(yōu)化選項(xiàng),release版應(yīng)該選擇Fastest, Smalllest,這個(gè)選項(xiàng)會(huì)開啟那些不增加代碼大小的全部?jī)?yōu)化,并讓可執(zhí)行文件盡可能小。

3. 避免編譯多個(gè)框架(但多個(gè)框架在不同機(jī)型上運(yùn)行速度相對(duì)快點(diǎn),iOS9 app thinning)

如果應(yīng)用需要在多種cpu架構(gòu)下運(yùn)行,那么xcode生成的二進(jìn)制文件會(huì)包含對(duì)應(yīng)架構(gòu)的多個(gè)副本,這樣可執(zhí)行文件的大小就會(huì)成倍增加。
arm cpu架構(gòu)可以標(biāo)識(shí)為armvX (64),X代表一個(gè)數(shù)字,如果不指定是64位,就是指32位。一般來說arm架構(gòu)都是向后兼容的,armv6的代碼可以在armv7上運(yùn)行,32位的代碼可以在64位cpu上運(yùn)行。(buidsettings->Architectures)

可執(zhí)行文件優(yōu)化

在項(xiàng)目里新建一個(gè)類,給它添加幾個(gè)方法,但不要在任何地方import它,build完項(xiàng)目后觀察linkmap,你會(huì)發(fā)現(xiàn)這個(gè)類還是被編譯進(jìn)可執(zhí)行文件了。
linkmap文件是xcode link時(shí)產(chǎn)生的中間文件,一般用于調(diào)試,可以精確知道某個(gè)地址對(duì)應(yīng)的函數(shù)。
對(duì)此我們可以通過腳本,遍歷整個(gè)項(xiàng)目的文件,找出所有沒有被引用的類文件和沒有被調(diào)用的方法,在保證沒有其他地方動(dòng)態(tài)調(diào)用的情況下把它們?nèi)サ?。如果整個(gè)項(xiàng)目歷時(shí)很長(zhǎng),歷時(shí)代碼遺留較多,這個(gè)清理對(duì)可執(zhí)行文件省出的空間還是挺可觀的。

可執(zhí)行文件的組成

XCode開啟編譯選項(xiàng)Write Link Map File
XCode -> Project -> Build Settings -> 搜map -> 把Write Link Map File選項(xiàng)設(shè)為yes,并指定好linkMap的存儲(chǔ)位置

屏幕快照 2016-07-05 下午6.08.47.png

編譯后,到編譯目錄里找到該txt文件,文件名和路徑就是上述的Path to Link Map File位于~/Library/Developer/Xcode/DerivedData/XXX-eumsvrzbvgfofvbfsoqokmjprvuh/Build/Intermediates/XXX.build/XXX-iphoneos/XXX.build/XXX-LinkMap-normal-armXX.text
其中XXX-eumsvrzbvgfofvbfsoqokmjprvuh的命名是不確定的這個(gè)LinkMap里展示了整個(gè)可執(zhí)行文件的全貌,列出了編譯后的每一個(gè).o目標(biāo)文件的信息(包括靜態(tài)鏈接庫.a里的),以及每一個(gè)目標(biāo)文件的代碼段,數(shù)據(jù)段存儲(chǔ)詳情。

1.在LinkMap里首先列出來的是目標(biāo)文件列表:

[0] linker synthesized
[1] /Users/zhaojijin/Library/Developer/Xcode/DerivedData/SimpleFinance-cvxujvtykofyxphauukoxkqhstcn/Build/Intermediates/SimpleFinance.build/Release-iphoneos/SimpleFinance.build/Objects-normal/arm64/UIImageView+HighlightedWebCache.o
[2] /Users/zhaojijin/Library/Developer/Xcode/DerivedData/SimpleFinance-cvxujvtykofyxphauukoxkqhstcn/Build/Intermediates/SimpleFinance.build/Release-iphoneos/SimpleFinance.build/Objects-normal/arm64/YKHomeWaitToBeMatchTitleItem.o

...

[1217] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/lib/libobjc.tbd
[1218] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/lib/libSystem.tbd
[1219] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks//Accelerate.framework/Accelerate.tbd

前面中括號(hào)里的是這個(gè)文件的編號(hào),后面會(huì)用到,像項(xiàng)目里引用到靜態(tài)鏈接庫里的目標(biāo)文件都會(huì)在這里列出來。

2.接著是一個(gè)段表,描述各個(gè)段在最后編譯成的可執(zhí)行文件中的偏移位置及大小,包括了代碼段(__TEXT,保存程序代碼段編譯后的機(jī)器碼)和數(shù)據(jù)段(__DATA,保存變量值)

    # Sections:
    # Address   Size        Segment Section
    0x100006660 0x0049CA0C  __TEXT  __text
    0x1004A306C 0x00002424  __TEXT  __stubs
    0x1004A5490 0x00002418  __TEXT  __stub_helper
    0x1004A78A8 0x00017790  __TEXT  __gcc_except_tab
    0x1004BF038 0x0005A3E7  __TEXT  __objc_methname
    0x100519420 0x00055D9C  __TEXT  __cstring
    0x10056F1BC 0x000082C0  __TEXT  __objc_classname
    0x10057747C 0x0000A9B8  __TEXT  __objc_methtype
    0x100581E40 0x0001BAE8  __TEXT  __const
    0x10059D928 0x0000436E  __TEXT  __ustring
    0x1005A1C98 0x0001235C  __TEXT  __unwind_info
    0x1005B4000 0x000006A8  __DATA  __got
    0x1005B46A8 0x00001818  __DATA  __la_symbol_ptr
    0x1005B5EC0 0x0001B710  __DATA  __const
    0x1005D15D0 0x0003B900  __DATA  __cfstring
    0x10060CED0 0x00002988  __DATA  __objc_classlist
    0x10060F858 0x00000028  __DATA  __objc_nlclslist
    0x10060F880 0x00000320  __DATA  __objc_catlist
    0x10060FBA0 0x00000018  __DATA  __objc_nlcatlist
    0x10060FBB8 0x000003D8  __DATA  __objc_protolist
    0x10060FF90 0x00000008  __DATA  __objc_imageinfo
    0x10060FF98 0x000FFB30  __DATA  __objc_const
    0x10070FAC8 0x000145F0  __DATA  __objc_selrefs
    0x1007240B8 0x00000080  __DATA  __objc_protorefs
    0x100724138 0x00002A28  __DATA  __objc_classrefs
    0x100726B60 0x00001B30  __DATA  __objc_superrefs
    0x100728690 0x00005814  __DATA  __objc_ivar
    0x10072DEA8 0x00019FA0  __DATA  __objc_data
    0x100747E48 0x00002E20  __DATA  __data
    0x10074AC68 0x00002120  __DATA  __bss
    0x10074D000 0x00000800  __DATA  __common

首列是數(shù)據(jù)在文件的偏移位置,第二列是這一段占用大小,第三列是段類型,代碼段和數(shù)據(jù)段,第四列是段名稱。

每一行的數(shù)據(jù)都緊跟在上一行后面,如第二行__symbol_stub的地址0x00275FD0就是第一行__text的地址0x00002740加上大小0x00273890,整個(gè)可執(zhí)行文件大致數(shù)據(jù)分布就是這樣。

這里可以清楚看到各種類型的數(shù)據(jù)在最終可執(zhí)行文件里占的比例,例如__text表示編譯后的程序執(zhí)行語句,__data表示已初始化的全局變量和局部靜態(tài)變量,__bss表示未初始化的全局變量和局部靜態(tài)變量,__cstring表示代碼里的字符串常量,等等。

3.接著就是按上表順序,列出具體的按每個(gè)文件列出每個(gè)對(duì)應(yīng)字段的位置和占用空間

    # Address   Size        File  Name
    0x100006660 0x00000018  [  1] -[UIImageView(HighlightedWebCache) sd_setHighlightedImageWithURL:]
    0x100006678 0x00000014  [  1] -[UIImageView(HighlightedWebCache) sd_setHighlightedImageWithURL:options:]
    0x10000668C 0x00000058  [  1] -[UIImageView(HighlightedWebCache) sd_setHighlightedImageWithURL:completed:]
    0x1000066E4 0x0000005C  [  1] -[UIImageView(HighlightedWebCache) sd_setHighlightedImageWithURL:options:completed:]
    ...

同樣首列是數(shù)據(jù)在文件的偏移地址,第二列是占用大小,第三列是所屬文件序號(hào),對(duì)應(yīng)上述Object files列表,最后是名字。

1. 去除無用代碼

在項(xiàng)目里新建一個(gè)類,給它添加幾個(gè)方法,但不要在任何地方import它,build完項(xiàng)目后觀察linkmap,你會(huì)發(fā)現(xiàn)這個(gè)類還是被編譯進(jìn)可執(zhí)行文件了。
對(duì)此我們可以通過腳本,遍歷整個(gè)項(xiàng)目的文件,找出所有沒有被引用的類文件和沒有被調(diào)用的方法,在保證沒有其他地方動(dòng)態(tài)調(diào)用的情況下把它們?nèi)サ簟H绻麄€(gè)項(xiàng)目歷時(shí)很長(zhǎng),歷時(shí)代碼遺留較多,這個(gè)清理對(duì)可執(zhí)行文件省出的空間還是挺可觀的。

2. 統(tǒng)計(jì)庫占用

項(xiàng)目里會(huì)引入很多第三方靜態(tài)庫,如果能知道這些第三方庫在可執(zhí)行文件里占用的大小,就可以評(píng)估是否值得去找替代方案去掉這個(gè)第三方庫。我們可以從linkmap中統(tǒng)計(jì)出這個(gè)信息,利用三方的node.js腳本,可以通過linkmap統(tǒng)計(jì)每個(gè).o目標(biāo)文件占用的體積和每個(gè).a靜態(tài)庫占用的體積,并進(jìn)行排序

3. 混淆類/方法名

觀察linkmap可以發(fā)現(xiàn)每個(gè)類和方法名都在__cstring段里都存了相應(yīng)的字符串值,所以類和方法名的長(zhǎng)短也是對(duì)可執(zhí)行文件大小是有影響的,原因還是object-c的動(dòng)態(tài)特性,因?yàn)樾枰ㄟ^類/方法名反射找到這個(gè)類/方法進(jìn)行調(diào)用,object-c對(duì)象模型會(huì)把類名,方法名列表都保存下來。
可以考慮在編譯前把所有類和方法名進(jìn)行混淆,把長(zhǎng)名字替換成短名字,這樣做的好處除了縮小體積外,還對(duì)安全性有很大提升,別人拿到可執(zhí)行文件對(duì)它c(diǎn)lass-dump出來的結(jié)果都是混淆后的類和方法名,就無法從類和方法名中猜出某個(gè)方法是做什么的,就難以掛鉤子進(jìn)行hack。不過這樣有個(gè)缺點(diǎn)就是crash堆棧反解出來的堆棧方法名會(huì)是混淆后的,需要再加一層混淆->原名的轉(zhuǎn)換,實(shí)現(xiàn)和使用成本有點(diǎn)高。

4. 減少冗余字符串

代碼上定義的所有靜態(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í)行文件里高很多。
替換NSLog為DLog:

image.png

類、方法名、屬性名等命名長(zhǎng)短影響包大小:

91FF2BF6-D5C3-4F00-89BC-ED4D6A3CD15F.png
5. ARC->MRC降低8%空間占用(可忽略,不實(shí)用)
image.png

homebrew

Homebrew官網(wǎng)

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
參考:

iOS工程瘦身

iOS微信安裝包瘦身

iOS可執(zhí)行文件瘦身方法

iOS 對(duì)源代碼進(jìn)行混淆

iOS APP可執(zhí)行文件的組成

用OCLint給iOS代碼做靜態(tài)分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 文章最后有我的 12 條小總結(jié)。 寫在前面 最近公司需求不多,正好研究一下 App 瘦身的辦法,寫了點(diǎn)小總結(jié)。 如...
    Damonwong閱讀 8,226評(píng)論 14 76
  • 縮減iOS安裝包大小是很多中大型APP都要做的事,一般首先會(huì)對(duì)資源文件下手,壓縮圖片/音頻,去除不必要的資源。這些...
    buptwsg閱讀 2,085評(píng)論 0 8
  • 更多內(nèi)容請(qǐng)挪步我的博客 圖片資源刪減 使用工具查找未引用資源 LSUnusedResources 比之前的 Unu...
    AliciaRain閱讀 732評(píng)論 1 0
  • iOS App瘦身 關(guān)于app瘦身,你能想到什么? 刪除無用類 刪除無用方法 代碼相似度分析 刪除無用圖片 無損壓...
    碼農(nóng)二哥閱讀 518評(píng)論 2 3
  • 《必然》第二章 知化(一) 知化:賦予對(duì)象認(rèn)知能力。 從目前人工智能技術(shù)的發(fā)展速度來看,現(xiàn)在出生的孩子在成年后可能...
    日子林光閱讀 562評(píng)論 3 2

友情鏈接更多精彩內(nèi)容