
資源優(yōu)化
1. 去除無用資源
一般都是版本迭代過程中存在的圖片資源。
可以借助三方工具來解決: Unused-master LSUnusedResources
通過Unused-master可以查找到一些工程中沒有用到的圖片,但這個(gè)工具并不是百分百正確,在圖片刪除的時(shí)候要慎重看一下。在掃描結(jié)果中,查找到了一些命名錯(cuò)誤的圖片,例如:xxx2x.png、xxx.png這樣的圖片命名方式會(huì)影響APP性能

2. 資源壓縮
2.1. 圖片無損壓縮
這個(gè)主要是利用三方工具ImageOptim來實(shí)現(xiàn):

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í)行無損壓縮。

關(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

這個(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ǔ)位置

編譯后,到編譯目錄里找到該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:

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

5. ARC->MRC降低8%空間占用(可忽略,不實(shí)用)

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