當(dāng)我們談?wù)搃OS瘦身的時(shí)候,我們到底在談?wù)撔┦裁?/h1>

不斷的開(kāi)發(fā)迭代痊土,產(chǎn)品經(jīng)理不斷的添加需求肄扎,引入的資源文件幾乎是只加不減,猛然回首,iOS包已經(jīng)100多m犯祠,看來(lái)iOS瘦身迫在眉睫靶竦取!:庠亍IΩ!

iOS瘦身的好處


我們先來(lái)討論 iOS瘦身的好處痰娱,正所謂知其然知其所以然弃榨。iOS瘦身有哪些好處?

1. However, consider download times when determining your app’s size. Minimize the file’s size as much as possible, keeping in mind that there is a 100 MB limit for over-the-air downloads. Abnormally large build files are usually the result of storing data, such as images, inside the compiled binary itself instead of as a resource inside your app bundle. If you are compiling an image or large dataset into your binary, it would be best to split this data out into a resource that is loaded dynamically by your app.

1.首先對(duì)于新用戶當(dāng)?shù)谝话惭b你的iOS程序時(shí)梨睁,需要下載完整的一個(gè).ipa文件鲸睛。(注意不同于升級(jí)),相同環(huán)境下iOS包越小用戶的下載時(shí)間越少坡贺,這一點(diǎn)對(duì)于用戶體驗(yàn)來(lái)說(shuō)至關(guān)重要官辈,特別是有些情況apple store訪問(wèn)不太順利,用戶下載你的iOS包肯定是迫切想了解使用你的產(chǎn)品遍坟,如果用戶等半天(等待對(duì)于用戶總是漫長(zhǎng)的)钧萍,用戶會(huì)煩躁帶著這種心情去了解你的產(chǎn)品,第一印象肯定不爽政鼠!

2.回想起來(lái)你的app第一個(gè)版本风瘦,有可能才10幾m,才一年多的時(shí)間公般,你的app已經(jīng)變成100m了万搔,然而產(chǎn)品業(yè)務(wù)需求是不斷迭代的,如果不加以控制官帘,后面只會(huì)越來(lái)越大瞬雹!

3.可能絕大部分的原因,你的產(chǎn)品并非是強(qiáng)勢(shì)app(比如支付寶刽虹,qq酗捌,百度)所以你也就不能隨便耍流氓,用戶手機(jī)的存儲(chǔ)空間是有限的(特別是那些16g的iPhone)涌哲,除了那些強(qiáng)勢(shì)app胖缤,你必須要在剩余不多的空間,爭(zhēng)取用戶不會(huì)刪除你的app(特別是那些工具類型app)阀圾!

4.less is more 哪廓,對(duì)于代碼和那些資源文件也是如此,無(wú)用的代碼和資源文件越多初烘,就導(dǎo)致項(xiàng)目冗余越大涡真,維護(hù)也越麻煩分俯,比如很可能同一張圖片有好幾個(gè)不同命名,如果要改圖片了就要全部更換 哆料!

知道了瘦身的好處接下來(lái)缸剪,我們就談?wù)撘幌耰OS該怎么瘦身

iOS該怎么瘦身 ?


在做任何相關(guān)優(yōu)化之前,我們需要做一些權(quán)衡东亦。通過(guò)權(quán)衡杏节,可以知道把優(yōu)化的重點(diǎn)集中在什么地方。我們上文提到讥此,當(dāng)?shù)谝话惭biOS程序時(shí)拢锹,需要下載完整的一個(gè).ipa文件。實(shí)際上.ipa文件就是一個(gè).zip結(jié)構(gòu)萄喳。簡(jiǎn)單的將后綴為.ipa文件修改為.zip卒稳,然后利用Finder將其解壓出來(lái)。右鍵單擊解壓出來(lái)的.app bundle他巨,選擇顯示包內(nèi)容充坑,以查看里面的資源文件。通過(guò)該方法我們可以看到哪些文件占的空間最大染突。記啄硪:.app bundle是經(jīng)過(guò)壓縮的,并且有些文件的壓縮效果要比別的文件好份企,所以壓縮后的效果才是才是最重要的也榄。不過(guò)一般情況下在壓縮前最大的文件,在壓縮后依舊是最大的文件司志。

而這些資源文件(包括圖片甜紫、聲音以及其它配置文件)通常占了ipa 很大部分,所以我首先針對(duì)資源文件優(yōu)化骂远。

(1).資源文件瘦身

a.刪除無(wú)用的圖片資源

一個(gè)項(xiàng)目開(kāi)發(fā)的越久囚霸,添加的功能模塊就越多,相應(yīng)的激才,也會(huì)慢慢引入更多的圖片資源拓型。但是在移除一些不再使用的模塊時(shí),開(kāi)發(fā)者往往不會(huì)將對(duì)應(yīng)的圖片資源一起刪除瘸恼,因?yàn)閳D片資源和源碼是分離的劣挫,長(zhǎng)久以來(lái),項(xiàng)目中就會(huì)出現(xiàn)大量沒(méi)有使用的圖片資源钞脂。刪除無(wú)用的資源一般來(lái)說(shuō) 能減掉個(gè)2~3MB揣云。 這個(gè)時(shí)候就要使用工具自動(dòng)迅速找出工程中所有沒(méi)被使用的資源文件嘍(想想就知道了 ,用手工得多慢多累氨小) 邓夕,工欲善其事,必先利其器阎毅。

我首先推薦的是https://github.com/tinymind/LSUnusedResources? 整個(gè)過(guò)程非常的快焚刚, 比shell腳本不知道方便到哪里去了, 為了照顧那些懶癌癥患者 我把使用方法也貼出來(lái)

使用方法如下

1.點(diǎn)擊 Browse.. 選擇一個(gè)文件夾扇调;

2.點(diǎn)擊 Search 開(kāi)始搜索矿咕;

3.等待片刻即可看到結(jié)果;

4.選中某些行狼钮,然后點(diǎn)擊 Delete 可以直接刪除資源

第二種方法就是你也可以用萬(wàn)能的腳本 https://github.com/examplecode/unused-image/blob/master/unused-image.sh? ?&&? http://stackoverflow.com/questions/6113243/how-to-find-unused-images-in-an-xcode-project/6113449#6113449

b.對(duì)資源壓縮

首先 盡量使用8-bit的PNG圖片碳柱,比32-bit的圖片能減少4倍的壓縮率。由于8-bit的圖片支持最多256種不同的顏色熬芜,所以8-bit的圖片一般只應(yīng)該用于一小部分的顏色圖片莲镣。例如灰度圖片最好使用8-bit。然后并不能事事都如意涎拉, 設(shè)計(jì)師提供的圖片資源往往都是直接從sketch中剪切后的資源瑞侮,大小非常大,這個(gè)時(shí)候就需要對(duì)png進(jìn)行無(wú)損壓縮鼓拧,假設(shè)我們的項(xiàng)目中有30M的圖片半火,然后將它們有損壓縮到80%的質(zhì)量,那么就可以減掉6MB左右季俩∨ヌ牵可以使用以下兩種方法進(jìn)行圖片的壓縮:用的是ImageOptim工具和compress命令(具體怎么使用我不想寫(xiě)了)。但是并不建議對(duì)資源做有損壓縮酌住,因?yàn)橛袚p壓縮通常壓縮后效果不盡人意需要設(shè)計(jì)一個(gè)個(gè)檢查店归。

c. BitCode

首先我們來(lái)介紹一下? BitCode 是啥?

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允許蘋(píng)果在后期重新優(yōu)化我們程序的二進(jìn)制文件,而不需要我們重新提交一個(gè)新的版本到App store上祭示。呀肄满,真有這么高級(jí)?

LLVM是目前蘋(píng)果采用的編譯器工具鏈,Bitcode是LLVM編譯器的中間代碼的一種編碼,LLVM的前端可以理解為C/C++/OC/Swift等編程語(yǔ)言,LLVM的后端可以理解為各個(gè)芯片平臺(tái)上的匯編指令或者可執(zhí)行機(jī)器指令數(shù)據(jù),那么,BitCode就是位于這兩者直接的中間碼. LLVM的編譯工作原理是前端負(fù)責(zé)把項(xiàng)目程序源代碼翻譯成Bitcode中間碼,然后再根據(jù)不同目標(biāo)機(jī)器芯片平臺(tái)轉(zhuǎn)換為相應(yīng)的匯編指令以及翻譯為機(jī)器碼.這樣設(shè)計(jì)就可以讓LLVM成為了一個(gè)編譯器架構(gòu),可以輕而易舉的在LLVM架構(gòu)之上發(fā)明新的語(yǔ)言(前端),以及在LLVM架構(gòu)下面支持新的CPU(后端)指令輸出,雖然Bitcode僅僅只是一個(gè)中間碼不能在任何平臺(tái)上運(yùn)行,但是它可以轉(zhuǎn)化為任何被支持的CPU架構(gòu),包括現(xiàn)在還沒(méi)被發(fā)明的CPU架構(gòu),也就是說(shuō)現(xiàn)在打開(kāi)Bitcode功能提交一個(gè)App到應(yīng)用商店,以后如果蘋(píng)果新出了一款手機(jī)并CPU也是全新設(shè)計(jì)的,在蘋(píng)果后臺(tái)服務(wù)器一樣可以從這個(gè)App的Bitcode開(kāi)始編譯轉(zhuǎn)化為新CPU上的可執(zhí)行程序,可供新手機(jī)用戶下載運(yùn)行這個(gè)App.

扯了這么多 质涛,推出Bitcode的好處是啥稠歉? 跟iOS瘦身啥關(guān)系?之前打包汇陆,可以運(yùn)行在各個(gè)不同型號(hào)的iOS設(shè)備上怒炸,是因?yàn)樵诖虬臅r(shí)候,蘋(píng)果幫我們把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ù)的中間件执庐。

d.正確導(dǎo)入圖片的姿勢(shì)


圖片的導(dǎo)入方式有如下幾種:

1.加入到Assets.xcassets中

只支持png格式的圖片

圖片只支持[UIImage imageNamed]的方式實(shí)例化,但是不能從Bundle中加載

在編譯時(shí)导梆,Images.xcassets中的所有文件會(huì)被打包為Assets.car的文件

2.CreateGroup

黃色文件夾圖標(biāo)轨淌;Xcode中分文件夾,Bundle中所有所在都在同一個(gè)文件夾下看尼,因此递鹉,不能出現(xiàn)文件重名的情況

可以直接使用[NSBundle mainBundle]作為資源路徑,效率高藏斩!

可以使用[UIImage imageNamed:]加載圖像

3.CreateFolderRefences

藍(lán)色文件夾躏结;Xcode中分文件夾,Bundle中同樣分文件夾灾茁,因此窜觉,可以出現(xiàn)文件重名的情況

需要在[NSBundle mainBundle]的基礎(chǔ)上拼接實(shí)際的路徑,效率較差

不能使用[UIImage imageNamed:]加載圖

4.PDFs矢量圖(Xcode6+)

5.Bundle(包)中的圖片素材

那這不同的導(dǎo)入方式北专,會(huì)對(duì)打出的包的大小有影響么禀挫?

經(jīng)過(guò)測(cè)試得知:CreateGroup、CreateFolderRefences兩種方式打出來(lái)的包拓颓,圖片都會(huì)直接放在.app文件中语婴,所以打包前后,圖片的大小不會(huì)改變

而加入到Assets.xcassets中的方法則不同驶睦,打包后砰左,在.app中會(huì)生成Assets.car文件來(lái)存儲(chǔ)Assets.xcassets中的圖片,并且文件大小方面也大大降低

所以场航,使用Assets.xcassets來(lái)管理圖片也可以達(dá)到ipa瘦身的效果~~

話說(shuō)PDFs矢量圖呢 缠导,利用矢量圖能不能幫助iOS App減少整體空間?

iOS對(duì)矢量圖的支持其實(shí)只是一種方便開(kāi)發(fā)者的選擇, 本質(zhì)上在XCode編譯的階段矢量圖會(huì)自動(dòng)生成對(duì)應(yīng)Target的@1x,@2x和@3x的png格式圖像溉痢。在iOS實(shí)際運(yùn)行中使用的圖片實(shí)際上已經(jīng)是png格式的圖片了~

用簡(jiǎn)單粗暴的實(shí)驗(yàn)來(lái)對(duì)比說(shuō)明, 步驟如下:

使用pdf原始文件編譯生成通用IPA

從生成的IPA文件中提取Asset.car文件

利用iOS Image Extractor提取Asset.car文件

將提取出來(lái)的@1x僻造、@2x、@3x放置回工程, 并刪除原始pdf中重新編譯

對(duì)比步驟1生成的car文件和步驟4生成的car文件大小

結(jié)果如下:

在iOS8.3以下, 相同壓縮比例的條件下, 矢量圖是無(wú)法幫助App減少空間孩饼。但是在iOS8.3以上, 利用xcassets可以避免多余的資源圖片下載, 只下載對(duì)應(yīng)的倍率的圖片髓削。因此, 嚴(yán)格意義下, 利用矢量圖并不能幫助App節(jié)省空間(其實(shí)跟用Assets.xcassets的方式效果差不多)。但是pdf矢量圖使用起來(lái)非常的方便, 建議使用镀娶。iOS本質(zhì)上并不支持矢量圖, 但是在編譯階段會(huì)將矢量圖轉(zhuǎn)化成目標(biāo)設(shè)備對(duì)應(yīng)的尺寸圖, 同時(shí)會(huì)利用xcassets的特性在iOS8.3以上設(shè)備下支持部分資源下載, 帶到包瘦身的效果立膛。每次都要讓UI給多個(gè)尺寸的圖, 肯定沒(méi)有給一張方便吧? 當(dāng)然, 前提是UI的童鞋是基于矢量圖工具制作的圖片的前提下~

簡(jiǎn)單的iOS瘦身技巧講完了 ,我們來(lái)點(diǎn)兒稍微高級(jí)的梯码,畢竟步子要一步一步走宝泵,邁步太大容易扯著蛋


(2).代碼級(jí)別的優(yōu)化

比如 在項(xiàng)目里新建一個(gè)類讲婚,給它添加幾個(gè)方法穴张,但不要在任何地方import它宜鸯,build完項(xiàng)目后觀察linkmap岖沛,你會(huì)發(fā)現(xiàn)這個(gè)類還是被編譯進(jìn)可執(zhí)行文件了罢坝。這是因?yàn)閛bject-c的runtime 性質(zhì)廓握,按C++的經(jīng)驗(yàn),沒(méi)有被使用到的類和方法編譯器都會(huì)優(yōu)化掉嘁酿,不會(huì)編進(jìn)最終的可執(zhí)行文件隙券,object-c不一樣,因?yàn)閛bject-c的動(dòng)態(tài)特性闹司,它可以通過(guò)類和方法名反射獲得這個(gè)類和方法進(jìn)行調(diào)用娱仔,所以就算在代碼里某個(gè)類沒(méi)被使用到,編譯器也沒(méi)法保證這個(gè)類不會(huì)在運(yùn)行時(shí)通過(guò)反射去調(diào)用游桩,所以只要是在項(xiàng)目里的文件牲迫,無(wú)論是否又被使用到都會(huì)被編譯進(jìn)可執(zhí)行文件又比如我們的項(xiàng)目里會(huì)引入很多第三方靜態(tài)庫(kù)借卧,如果能知道這些第三方庫(kù)在可執(zhí)行文件里占用的大小盹憎,就可以評(píng)估是否值得去找替代方案去掉這個(gè)第三方庫(kù)。

這個(gè)時(shí)候就要介紹一下LinkMap了铐刘,LinkMap文件是Xcode產(chǎn)生可執(zhí)行文件的同時(shí)生成的鏈接信息陪每,用來(lái)描述可執(zhí)行文件的構(gòu)造成分,包括代碼段(__TEXT)和數(shù)據(jù)段(__DATA)的分布情況镰吵。比如說(shuō)可執(zhí)行文件的構(gòu)成是怎樣檩禾,里面的內(nèi)容都是些什么,?

1疤祭、使用LinkMap文件對(duì)可執(zhí)行文件安裝包進(jìn)行分析

在xcode的設(shè)置中 Project->Build Settings->Write Link Map File為YES盼产,并設(shè)置Path to Link Map File,build完后就可以在設(shè)置的路徑看到LinkMap文件了

注意:此時(shí)最好使用真機(jī)進(jìn)行編譯勺馆,不然可能無(wú)法找到我們想要的文件戏售。

在以下目錄可以看到LinkMap文件,如下:

/Users/chenxintao/Library/Developer/Xcode/DerivedData/AppName-fnpgyspdoyxnotbpoliocmwypkff/Build/Intermediates/AppName.build/Debug-iphoneos/AppName.build/AppName-LinkMap-normal-arm64.txt

LinkMap文件主要分為以下三部分:

1.1 Object files

整個(gè)可執(zhí)行文件里包含的所有.O文件谓传,前面的數(shù)字是這個(gè).o文件的序號(hào)蜈项。樣式如下:

# Object files:

[0] linker synthesized

[1]/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/usr/lib/crt1.o

[2]/Users/bang/Library/Developer/Xcode/DerivedData/yishu-eyzgphknrrzpevagadjtwpzzeqag/Build/Intermediates/yishu.build/Debug-iphonesimulator/yishu.build/Objects-normal/i386/TKPFileInfo.o

...

[280] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANJob.o)

[281] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANWorker.o)

[282] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(MobClick.o)

[283] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANLaunch.o)

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

1.2 Sections

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

# Sections:

# Address? Size? ? Segment? Section

0x00002740 0x00273890 __TEXT __text

0x00275FD0 0x00000ADA __TEXT __symbol_stub

0x00276AAC 0x00001222 __TEXT __stub_helper

0x00277CCE 0x00019D9E __TEXT __objc_methname

0x00291A70 0x00012847 __TEXT __cstring

0x002A42B7 0x00001FC1 __TEXT __objc_classname

0x002A6278 0x000046A7 __TEXT __objc_methtype

0x002AA920 0x000061CE __TEXT __ustring

0x002B0AF0 0x00000764 __TEXT __const

0x002B1254 0x000028B8 __TEXT __gcc_except_tab

0x002B3B0C 0x00004EBC __TEXT __unwind_info

0x002B89C8 0x0003662C __TEXT __eh_frame

0x002EF000 0x00000014 __DATA __program_vars

0x002EF014 0x00000284 __DATA __nl_symbol_ptr

0x002EF298 0x0000073C __DATA __la_symbol_ptr

0x002EF9E0 0x000030A4 __DATA __const

0x002F2A84 0x00000590 __DATA __objc_classlist

0x002F3014 0x0000000C __DATA __objc_nlclslist

0x002F3020 0x0000006C __DATA __objc_catlist

0x002F308C 0x000000D8 __DATA __objc_protolist

0x002F3164 0x00000008 __DATA __objc_imageinfo

0x002F3170 0x0002BC80 __DATA __objc_const

0x0031EDF0 0x00003A30 __DATA __objc_selrefs

0x00322820 0x00000014 __DATA __objc_protorefs

0x00322834 0x000006B8 __DATA __objc_classrefs

0x00322EEC 0x00000394 __DATA __objc_superrefs

0x00323280 0x000037C8 __DATA __objc_data

0x00326A48 0x000096D0 __DATA __cfstring

0x00330118 0x00001424 __DATA __objc_ivar

0x00331540 0x00006080 __DATA __data

0x003375C0 0x0000001C __DATA __common

0x003375E0 0x000018E8 __DATA __bss

1.3 Symbols

可執(zhí)行文件中各種symbol的大小博个,包括各個(gè)symbol的起始地址怀樟,占用大小,來(lái)自哪一個(gè).o文件(使用之前提到的.o文件的序號(hào))盆佣。樣式如下:

# Address ? Size ?File ? ?Name

0x00002740 0x0000003E [ 1] start

0x00002780 0x00000400 [ 2] +[TKPFileInfo parseWithDictionary:]

0x00002B80 0x00000030 [ 2] -[TKPFileInfo fileID]

...

計(jì)算某個(gè).o文件在最終安裝包中占用的大小往堡,主要是解析Object files和Symbols兩個(gè)部分,從Object files讀取出每個(gè).o文件名和對(duì)應(yīng)的序號(hào)共耍,然后對(duì)Symbols中序號(hào)相同的文件的Size字段相加虑灰,即可得到每個(gè).o文件在最終包的大小。 同樣首列是數(shù)據(jù)在文件的偏移地址痹兜,第二列是占用大小穆咐,第三列是所屬文件序號(hào),對(duì)應(yīng)上述Object files列表字旭,最后是名字对湃。例如第二行代表了文件序號(hào)為2(反查上面就是TKPFileInfo.o)的parseWithDictionary方法占用了1000byte大小。

我們看到在這個(gè)里面除了可以看到DATA字段與TEXT字段的大小外遗淳,它還會(huì)列出所有類對(duì)象下的成員函數(shù)與類函數(shù)拍柒。其實(shí)這點(diǎn)很重要因?yàn)檫@樣我們就可以知道工程中所有實(shí)現(xiàn)的函數(shù)了洲脂。通過(guò)相應(yīng)的正則表達(dá)式斤儿,我們就可以提取出函數(shù)內(nèi)容,其正則表達(dá)式為[+|-][.+\s(.+)],然后我們通過(guò)另外一個(gè)強(qiáng)大的反編譯工具otool,可以提取出工程中所使用的函數(shù)列表(Used Selectors All)恐锦。

先說(shuō)那什么是otool呢?

Otool可以提取并顯示ios下目標(biāo)文件的相關(guān)信息往果,包括頭部,加載命令一铅,各個(gè)段陕贮,共享庫(kù),動(dòng)態(tài)庫(kù)等等潘飘。它擁有大量的命令選項(xiàng)肮之,是一個(gè)功能強(qiáng)大的分析工具,當(dāng)然還可以做反匯編的工具使用卜录。

說(shuō)到Otool就不得不提到mach-O 戈擒,那什么是mach-O??

Mach-O格式全稱為Mach Object文件格式的縮寫(xiě)艰毒,是mac上可執(zhí)行文件的格式筐高,類似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)。

Mach-o包含三個(gè)基本區(qū)域:

1.頭部(header structure)。Mach-o的頭部幫助內(nèi)核迅速確定當(dāng)前文件所支持的CPU架構(gòu)柑土。

2.加載命令(load command)蜀肘。

3.段(segment)』粒可以擁有多個(gè)段(segment)扮宠,每個(gè)段可以擁有零個(gè)或多個(gè)區(qū)域(section)。每一個(gè)段(segment)都擁有一段虛擬地址映射到進(jìn)程的地址空間狐榔。

4.鏈接信息坛增。一個(gè)完整的用戶級(jí)Mach-o文件的末端是鏈接信息。其中包含了動(dòng)態(tài)加載器用來(lái)鏈接可執(zhí)行文件或者依賴庫(kù)所需使用的符號(hào)表荒叼,字符串表等等轿偎。 如下圖左邊就是蘋(píng)果給出的mach-O格式的示意圖 ,第二個(gè)圖是我們使用machOView來(lái)分析某個(gè)可執(zhí)行文件中的armv7的格式被廓。可以看出他們兩者的關(guān)系是對(duì)應(yīng)的萝玷。



我們是如何找出工程中所使用的函數(shù)列表的呢嫁乘,其實(shí)就是使用命令字otool -V -s __DATA __objc_selrefs 項(xiàng)目.app/項(xiàng)目 | open -f。這里的項(xiàng)目地址指的是項(xiàng)目.app的路徑地址球碉,在Xcode7中的路徑為

/Users/用戶名/Library/Developer/Xcode/DerivedData/項(xiàng)目名/Build/Products/Debug-iphonesimulator/項(xiàng)目名.app/項(xiàng)目名

另外一個(gè)要注意的是-V要大寫(xiě)蜓斧,因?yàn)榇髮?xiě)和小寫(xiě)的命令是不一樣的。當(dāng)然大家也可以試試把DATA __objc_selrefs改成TEXT __objc_classname看看有什么不一樣睁冬。

下面就聊一聊如何對(duì)可執(zhí)行文件進(jìn)行瘦身挎春。

a.查找無(wú)用selector

結(jié)合LinkMap文件的__TEXT.__text,通過(guò)正則表達(dá)式([+|-][.+\s(.+)])豆拨,我們可以提取當(dāng)前可執(zhí)行文件里所有objc類方法和實(shí)例方法(SelectorsAll)直奋。再使用otool命令otool -v -s __DATA __objc_selrefs逆向__DATA.__objc_selrefs段,提取可執(zhí)行文件里引用到的方法名(UsedSelectorsAll)施禾,我們可以大致分析出SelectorsAll里哪些方法是沒(méi)有被引用的(SelectorsAll-UsedSelectorsAll)脚线。注意,系統(tǒng)API的Protocol可能被列入無(wú)用方法名單里弥搞,如UITableViewDelegate的方法邮绿,我們只需要對(duì)這些Protocol里的方法加入白名單過(guò)濾即可。

另外第三方庫(kù)的無(wú)用selector也可以這樣掃出來(lái)的攀例。

b. 查找無(wú)用oc類

查找無(wú)用oc類有兩種方式船逮,一種是類似于查找無(wú)用資源,通過(guò)搜索"[ClassName alloc/new"粤铭、"ClassName *"挖胃、"[ClassName class]"等關(guān)鍵字在代碼里是否出現(xiàn)。另一種是通過(guò)otool命令逆向__DATA.__objc_classlist段和__DATA.__objc_classrefs段來(lái)獲取當(dāng)前所有oc類和被引用的oc類,兩個(gè)集合相減就是無(wú)用oc類冠骄。

c.掃描重復(fù)代碼

可以利用第三方工具simian掃描(怎么使用自己去搜)伪煤。掃描重復(fù)代碼,但維護(hù)成本過(guò)高凛辣,因?yàn)樾枰貥?gòu)代碼抱既,沒(méi)有刪除代碼來(lái)得直接(看自己的夜霧需求)。

(3).編譯選項(xiàng)優(yōu)化

這個(gè)最有用的一個(gè)選項(xiàng)是Strip Linked Product / Deployment Postprocessing / Symbols Hidden by Default 在release版本應(yīng)該設(shè)為yes

原理是打開(kāi)這兩個(gè)選項(xiàng)后構(gòu)建ipa會(huì)去除掉symbol符號(hào)扁誓,就是那些類名啊函數(shù)名啊啥的防泵。這樣子的影響就是運(yùn)行時(shí)你沒(méi)法進(jìn)行線程回溯,符號(hào)都沒(méi)了回溯了也是亂碼蝗敢。但是不會(huì)影響正常的崩潰日志生成和解析捷泞。在本機(jī)專門(mén)測(cè)試過(guò),如果使用符號(hào)表來(lái)解析崩潰日志寿谴,則完全不受影響锁右。

第二個(gè) 就是 Build Settings->Optimization Level有幾個(gè)編譯優(yōu)化選項(xiàng),release版應(yīng)該選擇Fastest, Smalllest讶泰,這個(gè)選項(xiàng)會(huì)開(kāi)啟那些不增加代碼大小的全部?jī)?yōu)化咏瑟,并讓可執(zhí)行文件盡可能小(不過(guò)默認(rèn)就是如此)痪署。


終于你也看完了這么多內(nèi)容码泞,你以為總算看完了,呵呵狼犯,你還是太年輕了余寥,同志們注意了,我要開(kāi)始裝逼了悯森,


接下來(lái)我要把更高級(jí)點(diǎn)的 iOS瘦身(從方案來(lái)自滴滴大神的分享宋舷,說(shuō)實(shí)在的這也算是代碼級(jí)別的瘦身,這不過(guò)這種方法發(fā)揮了極致

眾所周知呐馆,代碼之間存在調(diào)用關(guān)系肥缔。假設(shè)iOS APP的主入口為-[UIApplication main],則所有開(kāi)發(fā)者的源代碼(包括第三方庫(kù))可分為兩類:存在一條調(diào)用路徑,使得代碼可以被主入口最終調(diào)用(稱此類代碼為被最終調(diào)用)汹来;不存在一條調(diào)用路徑续膳,使得代碼最終不能被主入口調(diào)用(稱此類代碼為未被最終調(diào)用)。

假設(shè)有一個(gè)源代碼級(jí)別的分析工具(或編譯器)收班,可以輔助分析代碼間的調(diào)用關(guān)系坟岔,這樣就使得分析最終被調(diào)用代碼成為可能,剩下的就是未被最終調(diào)用的代碼摔桦。

這種工具目前有成熟可用的嗎社付?答案是肯定的承疲,就是clang插件。除可用于分析未被最終調(diào)用代碼外鸥咖,clang還可輔助發(fā)現(xiàn)重復(fù)代碼燕鸽。作為L(zhǎng)LVM提供的編譯器前端,clang可將用戶的源代碼(C/C++/Objective-C)編譯成語(yǔ)言/目標(biāo)設(shè)備無(wú)關(guān)的IR(Intermediate Representation)實(shí)現(xiàn)啼辣。其可提供良好的插件支持啊研,容許用戶在編譯時(shí),運(yùn)行額外的自定義動(dòng)作鸥拧。(后來(lái)想想其實(shí)clang插件可以做更多的事情)

我們的目標(biāo)是使用clang插件減少包大小党远。其原理是,針對(duì)目標(biāo)工程富弦,基于clang的插件特性沟娱,開(kāi)發(fā)者可以編寫(xiě)插件以分析所有源代碼。編譯過(guò)程中腕柜,將插件作為clang的參數(shù)載入并生成各種中間文件济似。編譯完成后,還需編寫(xiě)一個(gè)工具去分析所有包含源碼的方法(包括用戶編寫(xiě)媳握,以及引入的第三方庫(kù)源代碼)碱屁,檢查這些方法中哪些最終可被程序主入口調(diào)用,剩余即是疑似無(wú)用代碼蛾找。簡(jiǎn)單的一個(gè)復(fù)查,移除那些確定無(wú)用的代碼赵誓,重新編譯打毛,便可以有效去除無(wú)用的代碼從而減少包大小。

首先“如何編寫(xiě)一個(gè)clang插件并集成到Xcode” (這個(gè)要不你們自己搜吧 實(shí)在篇幅命令很長(zhǎng) 俩功,但是按照命令一步一步來(lái)很簡(jiǎn)單的幻枉,沒(méi)啥技術(shù)含量)

第二“如何實(shí)現(xiàn)代碼級(jí)別的包瘦身” (代碼指的是OC中的形如-/+[Class method:\*]這種形式的代碼,調(diào)用關(guān)系典型如下:)

@interface ViewController : UIViewController

@end

@implementation ViewController

- (void)viewDidLoad {

[super viewDidLoad];

[self.view setBackgroundColor:[UIColor redColor]];

}

@end

則稱:-[ViewController viewDidLoad]調(diào)用了:

-[UIViewController viewDidLoad]

-[ViewController view](語(yǔ)法糖)

+[UIColor redColor]

-[UIView setBackgroundColor:]

我先緩緩啊 等會(huì)兒補(bǔ)充

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末诡蜓,一起剝皮案震驚了整個(gè)濱河市熬甫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔓罚,老刑警劉巖椿肩,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異豺谈,居然都是意外死亡郑象,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)茬末,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)厂榛,“玉大人,你說(shuō)我怎么就攤上這事』髂蹋” “怎么了辈双?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)柜砾。 經(jīng)常有香客問(wèn)我湃望,道長(zhǎng),這世上最難降的妖魔是什么局义? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任喜爷,我火速辦了婚禮,結(jié)果婚禮上萄唇,老公的妹妹穿的比我還像新娘檩帐。我一直安慰自己,他們只是感情好另萤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布湃密。 她就那樣靜靜地躺著,像睡著了一般四敞。 火紅的嫁衣襯著肌膚如雪泛源。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天忿危,我揣著相機(jī)與錄音达箍,去河邊找鬼。 笑死铺厨,一個(gè)胖子當(dāng)著我的面吹牛缎玫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播解滓,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼赃磨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了洼裤?” 一聲冷哼從身側(cè)響起邻辉,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腮鞍,沒(méi)想到半個(gè)月后值骇,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缕减,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年雷客,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桥狡。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡搅裙,死狀恐怖皱卓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情部逮,我是刑警寧澤娜汁,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站兄朋,受9級(jí)特大地震影響掐禁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜颅和,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一傅事、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧峡扩,春花似錦蹭越、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至案训,卻和暖如春买置,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背强霎。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工忿项, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人城舞。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓倦卖,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親椿争。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容