iOS編譯過(guò)程

背景

隨著需求爆發(fā),代碼和圖片資源越來(lái)越多分瘦,包體積越來(lái)越大蘸泻,用戶下載成本越來(lái)越高,瘦包迫在眉睫嘲玫,要想瘦包蟋恬,就需要知道包由何組成,每個(gè)組成部分又是怎么來(lái)的趁冈,這就必須了解編譯過(guò)程,當(dāng)然有人會(huì)說(shuō)拜马,不就瘦包嘛渗勘,網(wǎng)上有教程呀,巴拉巴拉照著做就行了俩莽!
嗯~那證明app的包還不夠復(fù)雜旺坠,當(dāng)你需要引用上百個(gè)自研或是開(kāi)源庫(kù)的時(shí)候自會(huì)明白,我們來(lái)看下文件組成

iOS應(yīng)用的文件目錄結(jié)構(gòu)

一個(gè)app通常有如下幾個(gè)模塊組成:源碼編譯后的二進(jìn)制(靜態(tài)庫(kù)包含在其中)扮超、動(dòng)態(tài)庫(kù)取刃、bundle等資源文件、plist等配置文件出刷、代碼簽名文件CodeResources

  • 源碼編譯后的二進(jìn)制
    靜態(tài)庫(kù)是單獨(dú)編譯的璧疗,在源碼文件編譯完成后會(huì)由靜態(tài)鏈接器一起打包生成最終的Mach-O格式的二進(jìn)制文件,這就意味著多份相同的靜態(tài)庫(kù)是沒(méi)有辦法鏈接通過(guò)的馁龟,有一種辦法可以解決這個(gè)問(wèn)題崩侠,就是同一份靜態(tài)庫(kù)包裝到不同的動(dòng)態(tài)庫(kù)中以形成隔離,因?yàn)閯?dòng)態(tài)庫(kù)在app編譯完成后不會(huì)一起進(jìn)行靜態(tài)鏈接并且符號(hào)表也是與app分開(kāi)的坷檩,在集成三方庫(kù)的時(shí)候可能會(huì)遇到這個(gè)問(wèn)題

  • 動(dòng)態(tài)庫(kù)
    動(dòng)態(tài)庫(kù)也是單獨(dú)編譯的却音,我們程序中用到的所有系統(tǒng)級(jí)別的framework都是動(dòng)態(tài)庫(kù),它們集成在操作系統(tǒng)中矢炼,不會(huì)占用app的存儲(chǔ)空間系瓢,自研的動(dòng)態(tài)庫(kù)是集成在app目錄下的Frameworks中,編譯時(shí)句灌,動(dòng)態(tài)庫(kù)的物理路徑需要配置到app的編譯設(shè)置的搜索路徑夷陋,在App編譯的時(shí)候就會(huì)被拷貝到app的壓縮包里的Frameworks文件夾下,符號(hào)表單獨(dú)保存并且需要上傳到蘋(píng)果的后臺(tái)

  • bundle
    程序的圖片資源文件

  • plist配置文件
    程序的標(biāo)識(shí)符以及系統(tǒng)服務(wù)的訪問(wèn)權(quán)限等都配置在里面

  • 代碼簽名
    就是存儲(chǔ)通過(guò)證書(shū)以及加密算法生成簽名密鑰的文件

可執(zhí)行文件的物理結(jié)構(gòu)

可執(zhí)行文件包含了機(jī)器指令代碼、數(shù)據(jù)肌稻、符號(hào)表清蚀、調(diào)試信息等,目標(biāo)文件以的形式存儲(chǔ)以上信息爹谭,目標(biāo)文件通常包括:文件頭枷邪、text段、data段诺凡、bss段

  • 文件頭
    描述整個(gè)文件的屬性(包含目標(biāo)機(jī)器架構(gòu)东揣、是動(dòng)態(tài)鏈接還是靜態(tài)鏈接以及是否是可執(zhí)行文件)和一個(gè)段表
    段表就是存儲(chǔ)段的數(shù)組,描述了各個(gè)段在文件中的偏移量以及段本身的屬性

  • text段
    源代碼編譯后的機(jī)器指令被存放在text段腹泌,又叫做代碼段

  • data段
    初始化的全局變量和靜態(tài)變量被放在data段嘶卧,又叫做數(shù)據(jù)段

  • bss段
    未初始化的全局變量和靜態(tài)變量被放在bss段,和data段一起被稱(chēng)為數(shù)據(jù)段

符號(hào)表
符號(hào)通常指函數(shù)和變量(還有其他符號(hào)比如段名凉袱、行號(hào)信息等)芥吟,編譯的最后一個(gè)階段靜態(tài)鏈接,就是符號(hào)的處理過(guò)程专甩,每個(gè)目標(biāo)文件都有一個(gè)符號(hào)表钟鸵,記錄了目標(biāo)文件里面所有的符號(hào)以及對(duì)應(yīng)的符號(hào)值,對(duì)于函數(shù)和變量來(lái)講符號(hào)值就是他們的地址涤躲,符號(hào)表在程序編譯之初就創(chuàng)建棺耍,在整個(gè)編譯過(guò)程中都起著至關(guān)重要的作用,在靜態(tài)鏈接完成之后會(huì)生成最終的符號(hào)表种樱,動(dòng)態(tài)庫(kù)由于不參與主可執(zhí)行文件的靜態(tài)鏈接過(guò)程蒙袍,所以符號(hào)表是抽離的

調(diào)試信息
編譯器支持源代碼級(jí)別的調(diào)試,debug環(huán)境下可以在程序中打斷點(diǎn)調(diào)試嫩挤,release環(huán)境下編譯器會(huì)過(guò)濾掉調(diào)試信息害幅,即編譯后的二進(jìn)制里面沒(méi)有調(diào)試代碼,因此斷點(diǎn)是無(wú)效的

可執(zhí)行程序二進(jìn)制(包含了靜態(tài)庫(kù))俐镐、動(dòng)態(tài)庫(kù)都是編譯器的產(chǎn)物矫限,那么為什么需要編譯呢,源碼不可以直接執(zhí)行嗎

Object-C為什么需要編譯

  • 解釋型語(yǔ)言
    解釋型語(yǔ)言運(yùn)行時(shí)實(shí)時(shí)被解釋器解析為機(jī)器碼并且執(zhí)行一次就需要解析一次佩抹,腳本語(yǔ)言如JavaScript等都是解釋型語(yǔ)言叼风,優(yōu)點(diǎn)是省去了編譯過(guò)程,但是運(yùn)行效率低棍苹,雖然當(dāng)代瀏覽器解釋器經(jīng)過(guò)深度優(yōu)化无宿,但是相比直接運(yùn)行可執(zhí)行二進(jìn)制來(lái)講也會(huì)慢很多
  • 編譯型語(yǔ)言
    有一個(gè)復(fù)雜又耗時(shí)的編譯過(guò)程,最終生成的二進(jìn)制機(jī)器碼枢里,可以被處理器直接識(shí)別并執(zhí)行孽鸡,相比解釋性語(yǔ)言來(lái)說(shuō)運(yùn)行時(shí)效率高了很多

Object-C是一門(mén)編譯型語(yǔ)言蹂午,底層通過(guò)c、c++彬碱、匯編實(shí)現(xiàn)豆胸,上層架設(shè)了一層語(yǔ)法糖,配合強(qiáng)大的運(yùn)行時(shí)庫(kù)使用

編譯原理

一句話概括:從源碼生成二進(jìn)制機(jī)器碼的過(guò)程就是編譯過(guò)程巷疼,整個(gè)過(guò)程分為四個(gè)階段:預(yù)編譯晚胡、編譯、匯編嚼沿、靜態(tài)鏈接

  • 預(yù)編譯:
    進(jìn)行宏替換估盘、頭文件包含、條件編譯識(shí)別等工作
  • 詞法分析:
    將源代碼的字符序列分割成一些列的記號(hào)骡尽,包含關(guān)鍵字遣妥、標(biāo)識(shí)符、字面量攀细、符號(hào)箫踩,同時(shí)將標(biāo)識(shí)符存放到符號(hào)表,常量存放到文字表以備后續(xù)使用
  • 語(yǔ)法分析:
    把上一步產(chǎn)生的記號(hào)解析成一個(gè)以表達(dá)式為節(jié)點(diǎn)的抽象語(yǔ)法樹(shù)(AST)谭贪,這個(gè)階段會(huì)進(jìn)行表達(dá)式合法性校驗(yàn)班套,比如缺少操作符、括弧不匹配等編譯器會(huì)直接給出錯(cuò)誤提示故河,當(dāng)然xcode在沒(méi)有bulid之前也會(huì)進(jìn)行語(yǔ)法檢測(cè),這也是衡量一款開(kāi)發(fā)工具是否合格的基本要求
  • 語(yǔ)義分析:
    編譯器只能分析靜態(tài)語(yǔ)義吆豹,即編譯期間就可以確定的語(yǔ)義鱼的,通常包括類(lèi)型匹配、轉(zhuǎn)換痘煤,這個(gè)階段編譯器會(huì)給出類(lèi)型不匹配等警告凑阶,不會(huì)報(bào)錯(cuò),因?yàn)檫\(yùn)行時(shí)基本類(lèi)型不匹配實(shí)際會(huì)進(jìn)行精度取舍衷快,指針類(lèi)型不匹配會(huì)以指針?biāo)赶虻恼鎸?shí)對(duì)象去調(diào)用方法宙橱,這些都是動(dòng)態(tài)語(yǔ)義,不在編譯器的管理范疇蘸拔,當(dāng)然也沒(méi)有這個(gè)能力
  • 中間代碼生成:
    由于直接在語(yǔ)法樹(shù)上做優(yōu)化比較困難师郑,編譯器會(huì)將整個(gè)語(yǔ)法樹(shù)轉(zhuǎn)換成中間代碼,它是語(yǔ)法樹(shù)的順序表示调窍,至此已經(jīng)生成接近目標(biāo)代碼的匯編代碼宝冕,只是還與目標(biāo)機(jī)器的運(yùn)行時(shí)環(huán)境無(wú)關(guān),比如機(jī)器字長(zhǎng)邓萨、變量地址地梨、寄存器名稱(chēng)等菊卷,因此中間代碼是編譯前后端的分割線,前端負(fù)責(zé)產(chǎn)生中間代碼宝剖,后端負(fù)責(zé)將其轉(zhuǎn)換為目標(biāo)代碼洁闰,這也使得編譯器跨平臺(tái)成為可能
  • 目標(biāo)代碼生成與優(yōu)化:
    目標(biāo)代碼生成器會(huì)根據(jù)目標(biāo)機(jī)器的運(yùn)行環(huán)境將中間代碼轉(zhuǎn)換為目標(biāo)代碼,目標(biāo)代碼優(yōu)化器會(huì)對(duì)匯編碼進(jìn)行優(yōu)化万细,比如循環(huán)優(yōu)化扑眉、多余指令刪除、尋址優(yōu)化雅镊、用位移操作代替乘法運(yùn)算等
  • 匯編:
    匯編器是編譯后端的后端襟雷,負(fù)責(zé)將匯編碼轉(zhuǎn)化為機(jī)器碼
  • 靜態(tài)鏈接:
    編譯器會(huì)把源代碼編譯成一個(gè)一個(gè)的目標(biāo)文件.o文件,定義在A.o文件中的變量及函數(shù)在B.o文件中是無(wú)法得到地址的仁烹,編譯器會(huì)用0填充耸弄,待到鏈接的時(shí)候由鏈接器進(jìn)行修正,這個(gè)過(guò)程叫做重定位卓缰,最終靜態(tài)鏈接器把編譯產(chǎn)生的所有.o文件和靜態(tài)庫(kù)一起打包生成一個(gè)Mach-O格式的二進(jìn)制文件

減小包體積

我們以目標(biāo)為導(dǎo)向计呈,瘦包需要瘦哪些模塊:可執(zhí)行二進(jìn)制文件、動(dòng)態(tài)庫(kù)征唬、Bundle資源

  • 第一招:??
    前面優(yōu)化圖片資源會(huì)帶來(lái)很多驚喜捌显,掃描無(wú)用資源文件直接刪除和壓縮Bundle中的圖片,可能分分鐘降下來(lái)幾十兆总寒,一期目標(biāo)直接達(dá)成扶歪,接下來(lái)就要對(duì)代碼部分動(dòng)刀了
  • 第二招:?
    經(jīng)過(guò)上面的編譯原理我們知道不同架構(gòu)的目標(biāo)機(jī)器編譯出來(lái)的目標(biāo)代碼是不一樣的,iOS的app是一個(gè)胖架構(gòu)包摄闸,可以包含很多架構(gòu)在里面善镰,打包送上應(yīng)用商店后蘋(píng)果會(huì)對(duì)包進(jìn)行拆分,不同架構(gòu)的手機(jī)下載對(duì)應(yīng)架構(gòu)的包年枕,因此減少app兼容的架構(gòu)是不能夠減小包體積的
  • 第三招:???參半
    掃描刪減無(wú)用代碼炫欺,合并類(lèi)似功能的冗余代碼,這招對(duì)于源文件熏兄、動(dòng)態(tài)庫(kù)都會(huì)奏效品洛,對(duì)于靜態(tài)庫(kù)來(lái)講未必奏效,因?yàn)殪o態(tài)庫(kù)在鏈接的時(shí)候只有用到的部分才會(huì)打包到可執(zhí)行二進(jìn)制
  • 第四招:???需要根據(jù)包本身的體積衡量
    多個(gè)動(dòng)態(tài)庫(kù)引用了相同一份靜態(tài)庫(kù)會(huì)分別在動(dòng)態(tài)庫(kù)中鏈接到使用的部分摩桶,換句話說(shuō)靜態(tài)庫(kù)被動(dòng)態(tài)庫(kù)使用的部分會(huì)打包到動(dòng)態(tài)庫(kù)中桥状,因此多個(gè)動(dòng)態(tài)庫(kù)就打包了多份靜態(tài)庫(kù)(當(dāng)然只有使用到的部分),這里可以做個(gè)優(yōu)化把靜態(tài)庫(kù)變成動(dòng)態(tài)庫(kù)硝清,這樣就是所有動(dòng)態(tài)庫(kù)引用一份動(dòng)態(tài)庫(kù)岛宦,不會(huì)出現(xiàn)多份冗余的情況,當(dāng)然要考慮到庫(kù)包本身的大小耍缴,如果太大做成動(dòng)態(tài)庫(kù)反而會(huì)增大包體積砾肺,因?yàn)閯?dòng)態(tài)庫(kù)是全量拷貝到app包內(nèi)的
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挽霉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子变汪,更是在濱河造成了極大的恐慌侠坎,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裙盾,死亡現(xiàn)場(chǎng)離奇詭異实胸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)番官,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)庐完,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人徘熔,你說(shuō)我怎么就攤上這事门躯。” “怎么了酷师?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵讶凉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我山孔,道長(zhǎng)懂讯,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任台颠,我火速辦了婚禮褐望,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘串前。我一直安慰自己譬挚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布酪呻。 她就那樣靜靜地躺著,像睡著了一般盐须。 火紅的嫁衣襯著肌膚如雪玩荠。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,604評(píng)論 1 305
  • 那天贼邓,我揣著相機(jī)與錄音阶冈,去河邊找鬼。 笑死塑径,一個(gè)胖子當(dāng)著我的面吹牛女坑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播统舀,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼匆骗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼劳景!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起碉就,我...
    開(kāi)封第一講書(shū)人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤盟广,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后瓮钥,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體筋量,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年碉熄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桨武。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锈津,死狀恐怖呀酸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情一姿,我是刑警寧澤七咧,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站叮叹,受9級(jí)特大地震影響艾栋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛉顽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一蝗砾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧携冤,春花似錦悼粮、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至翘地,卻和暖如春申尤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背衙耕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工昧穿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人橙喘。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓时鸵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親厅瞎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子饰潜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

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

  • iOS 開(kāi)發(fā)中 Objective-C 是 Clang / LLVM 來(lái)編譯的初坠。swift 是 Swift / L...
    forping閱讀 970評(píng)論 0 0
  • 前言 語(yǔ)言類(lèi)型 我們有很多維度可以將計(jì)算機(jī)語(yǔ)言進(jìn)行分類(lèi),其中以編譯/執(zhí)行方式為維度囊拜,可以將計(jì)算機(jī)語(yǔ)言分為: 編譯型...
    AiLearn閱讀 2,400評(píng)論 1 6
  • 前言 一般可以將編程語(yǔ)言分為兩種某筐,編譯語(yǔ)言和直譯式語(yǔ)言。像C++,Objective C都是編譯語(yǔ)言冠跷。編譯語(yǔ)言在執(zhí)...
    cosWriter閱讀 2,330評(píng)論 1 28
  • 前言 一般可以將編程語(yǔ)言分為兩種南誊,編譯語(yǔ)言和直譯式語(yǔ)言。 像C++,Objective C都是編譯語(yǔ)言蜜托。編譯語(yǔ)言在...
    一川煙草i蓑衣閱讀 10,358評(píng)論 1 27
  • 來(lái)源:黃文臣 前言 一般可以將編程語(yǔ)言分為兩種抄囚,編譯語(yǔ)言和直譯式語(yǔ)言。像C++,Objective C都是編譯語(yǔ)言...
    哦呵呵y閱讀 1,270評(píng)論 0 6