資源優(yōu)化
1. 去除無用資源
一般都是版本迭代過程中存在的圖片資源喂窟。
可以借助三方工具來解決: Unused-master LSUnusedResources
通過Unused-master可以查找到一些工程中沒有用到的圖片,但這個工具并不是百分百正確揪垄,在圖片刪除的時候要慎重看一下遵蚜。在掃描結果中扒腕,查找到了一些命名錯誤的圖片吧彪,例如:xxx2x.png、xxx.png這樣的圖片命名方式會影響APP性能
2. 資源壓縮
2.1. 圖片無損壓縮
這個主要是利用三方工具ImageOptim來實現(xiàn):
ImageOptim Mac版是一款非常簡單的圖片大小優(yōu)化工具出刷。只要拖動圖片到軟件界面就可以自動把圖片的大小進行優(yōu)化璧疗。ImageOptim Mac版對于開發(fā)人員和設計人員一定還有用處,如文件的EXIF標簽和顏色配置文件等馁龟,達到優(yōu)化減小占用磁盤空間崩侠。
圖片文件中往往包含一些注釋、顏色 Profile 等多余信息屁柏,移除后圖像質量不變啦膜,體積更小載入更快有送。ImageOptim 以此方式壓縮圖片,先分析圖片僧家,找到最優(yōu)壓縮參數(shù)雀摘,去除無關信息減小體積贯莺,實行無損壓縮襟衰。
關于圖片資源補充:
1.能用縮略圖的用縮略圖、單一色彩可以用純色代替
2.盡量使用8-bit圖片使用8-bit的PNG圖片鹤啡,比32-bit的圖片能減少4倍的壓縮率肌稻。 由于8-bit的圖片支持最多256種不同的顏色清蚀,所以8-bit的圖片一般只應該用于一小 部分的顏色圖片。例如灰度圖片最好使用8-bit爹谭。
2.2. 音頻壓縮
參考WWDC中的Audio Development for Games枷邪,里面介紹了如何有效的處理音頻。常規(guī)來說诺凡,我們要使用AAC或MP3來壓縮音頻东揣,并且可以嘗試降低一下音頻的比特率。有時候44.1khz的采樣是沒有必要的腹泌,稍微低一點的比特率也不會降低音頻的質量嘶卧。
2.3. 查找文件大小
通過查找工程中文件的大小,可以找到一些較大的圖片和三方庫凉袱,視情況壓縮或替換這個主要是用命令行芥吟,cd到工程目錄 然后輸入find . -size +100 就會遍歷出工程目錄下大于100k的文件,如果是+1000 就是大于1000k的专甩,以此類推...
例如:Robin:SimpleFinance zhaojijin$ find . -size +1000
這個方法掃描到的文件并不是完全正確钟鸵,如上圖,這個結果中changeCard_lostCardStepOnePic@3x.png finder中實際大小是854k配深,但這并不影響使用携添,大體還是對的
3.不常用資源換為下載
比如自定義字體,可以在APP第一次啟動后動態(tài)獲取
編譯優(yōu)化
1. 去除符號信息
Strip Debug Symbols During Copy 和 Symbols Hidden by Default 在release版本應該設為yes篓叶,可以去除不必要的調試符號,當然xcode默認就是yes
2. 開啟編譯優(yōu)化
Build Settings->Optimization Level有幾個編譯優(yōu)化選項,release版應該選擇Fastest, Smalllest羞秤,這個選項會開啟那些不增加代碼大小的全部優(yōu)化缸托,并讓可執(zhí)行文件盡可能小。
3. 避免編譯多個框架(但多個框架在不同機型上運行速度相對快點,iOS9 app thinning)
如果應用需要在多種cpu架構下運行瘾蛋,那么xcode生成的二進制文件會包含對應架構的多個副本俐镐,這樣可執(zhí)行文件的大小就會成倍增加。
arm cpu架構可以標識為armvX (64)哺哼,X代表一個數(shù)字佩抹,如果不指定是64位叼风,就是指32位。一般來說arm架構都是向后兼容的棍苹,armv6的代碼可以在armv7上運行无宿,32位的代碼可以在64位cpu上運行。(buidsettings->Architectures)
可執(zhí)行文件優(yōu)化
在項目里新建一個類枢里,給它添加幾個方法孽鸡,但不要在任何地方import它,build完項目后觀察linkmap栏豺,你會發(fā)現(xiàn)這個類還是被編譯進可執(zhí)行文件了彬碱。
linkmap文件是xcode link時產生的中間文件,一般用于調試奥洼,可以精確知道某個地址對應的函數(shù)巷疼。
對此我們可以通過腳本,遍歷整個項目的文件灵奖,找出所有沒有被引用的類文件和沒有被調用的方法嚼沿,在保證沒有其他地方動態(tài)調用的情況下把它們去掉。如果整個項目歷時很長桑寨,歷時代碼遺留較多伏尼,這個清理對可執(zhí)行文件省出的空間還是挺可觀的。
可執(zhí)行文件的組成
XCode開啟編譯選項Write Link Map File
XCode -> Project -> Build Settings -> 搜map -> 把Write Link Map File選項設為yes尉尾,并指定好linkMap的存儲位置
編譯后爆阶,到編譯目錄里找到該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的命名是不確定的這個LinkMap里展示了整個可執(zhí)行文件的全貌沙咏,列出了編譯后的每一個.o目標文件的信息(包括靜態(tài)鏈接庫.a里的)辨图,以及每一個目標文件的代碼段,數(shù)據(jù)段存儲詳情肢藐。
1.在LinkMap里首先列出來的是目標文件列表:
[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
前面中括號里的是這個文件的編號故河,后面會用到,像項目里引用到靜態(tài)鏈接庫里的目標文件都會在這里列出來吆豹。
2.接著是一個段表鱼的,描述各個段在最后編譯成的可執(zhí)行文件中的偏移位置及大小,包括了代碼段(__TEXT痘煤,保存程序代碼段編譯后的機器碼)和數(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环葵,整個可執(zhí)行文件大致數(shù)據(jù)分布就是這樣。
這里可以清楚看到各種類型的數(shù)據(jù)在最終可執(zhí)行文件里占的比例宝冕,例如__text表示編譯后的程序執(zhí)行語句张遭,__data表示已初始化的全局變量和局部靜態(tài)變量,__bss表示未初始化的全局變量和局部靜態(tài)變量猬仁,__cstring表示代碼里的字符串常量帝璧,等等。
3.接著就是按上表順序湿刽,列出具體的按每個文件列出每個對應字段的位置和占用空間
# 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ù)在文件的偏移地址的烁,第二列是占用大小,第三列是所屬文件序號诈闺,對應上述Object files列表渴庆,最后是名字。
1. 去除無用代碼
在項目里新建一個類雅镊,給它添加幾個方法襟雷,但不要在任何地方import它,build完項目后觀察linkmap仁烹,你會發(fā)現(xiàn)這個類還是被編譯進可執(zhí)行文件了耸弄。
對此我們可以通過腳本,遍歷整個項目的文件卓缰,找出所有沒有被引用的類文件和沒有被調用的方法计呈,在保證沒有其他地方動態(tài)調用的情況下把它們去掉。如果整個項目歷時很長征唬,歷時代碼遺留較多捌显,這個清理對可執(zhí)行文件省出的空間還是挺可觀的。
2. 統(tǒng)計庫占用
項目里會引入很多第三方靜態(tài)庫总寒,如果能知道這些第三方庫在可執(zhí)行文件里占用的大小扶歪,就可以評估是否值得去找替代方案去掉這個第三方庫。我們可以從linkmap中統(tǒng)計出這個信息摄闸,利用三方的node.js腳本善镰,可以通過linkmap統(tǒng)計每個.o目標文件占用的體積和每個.a靜態(tài)庫占用的體積,并進行排序
3. 混淆類/方法名
觀察linkmap可以發(fā)現(xiàn)每個類和方法名都在__cstring段里都存了相應的字符串值年枕,所以類和方法名的長短也是對可執(zhí)行文件大小是有影響的媳禁,原因還是object-c的動態(tài)特性,因為需要通過類/方法名反射找到這個類/方法進行調用画切,object-c對象模型會把類名,方法名列表都保存下來囱怕。
可以考慮在編譯前把所有類和方法名進行混淆霍弹,把長名字替換成短名字毫别,這樣做的好處除了縮小體積外,還對安全性有很大提升典格,別人拿到可執(zhí)行文件對它class-dump出來的結果都是混淆后的類和方法名岛宦,就無法從類和方法名中猜出某個方法是做什么的,就難以掛鉤子進行hack耍缴。不過這樣有個缺點就是crash堆棧反解出來的堆棧方法名會是混淆后的砾肺,需要再加一層混淆->原名的轉換,實現(xiàn)和使用成本有點高防嗡。
4. 減少冗余字符串
代碼上定義的所有靜態(tài)字符串都會記錄在在可執(zhí)行文件的__cstring段变汪,如果項目里Log非常多,這個空間占用也是可觀的蚁趁,也有幾百K的大小裙盾,可以考慮清理所有冗余的字符串。另外如果有特別長的字符串他嫡,建議抽離保存成靜態(tài)文件番官,因為AppStore對可執(zhí)行文件加密導致壓縮率低,特別長的字符串抽離成靜態(tài)資源文件后壓縮率會比在可執(zhí)行文件里高很多钢属。
替換NSLog為DLog:
類徘熔、方法名、屬性名等命名長短影響包大小:
5. ARC->MRC降低8%空間占用(可忽略淆党,不實用)
homebrew
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"