iOS編譯原理

主要內(nèi)容:

  1. 理解C错邦、C++以及OC的關(guān)系
  2. 編譯型語言與解釋型語言
  3. 編譯器LLVMCLang
  4. 理解iOS編譯流程
  5. 預處理
  6. 編譯
  7. 匯編
  8. 鏈接

一腻豌、理解C家坎、C++以及OC的關(guān)系

1.C語言
  1. C語言是一門面向過程的計算機編程語言嘱能,既可用于系統(tǒng)軟件開發(fā),也適用于應(yīng)用軟件開發(fā)虱疏;
  2. C語言編譯器普遍存在于各種不同的操作系統(tǒng)中惹骂,例如Microsoft Windows,Mac OS X, Linux, Unix等;
  3. C語言的設(shè)計影響了眾多后來的編程語言做瞪,例如C++对粪、Objective-CJava装蓬、C#等著拭;
2.C++語言
  1. 兼容了C語言面向過程特點,但又進行了擴充和完善牍帚;
  2. 作為一種面向?qū)ο蟮恼Z言儡遮,具有封裝、多繼承暗赶、多態(tài)等特性鄙币;
3.Objective-C語言
  1. 擴展了C語言的能力,使其具備面向?qū)ο笤O(shè)計的能力蹂随,相當于C的超集十嘿;
  2. OC代碼中也可以有CC++語句,它可以調(diào)用C函數(shù)岳锁,也可以通過C++對象訪問方法详幽;
4.OC與C++的比較
  1. OCC++都是從C語言演變而來面向?qū)ο笤O(shè)計語言,也都兼容標準的C語言浸锨;但它們屬于不同的面向?qū)ο髮W派唇聘;
  2. 兩者最大的不同在于:OC提供了運行時的動態(tài)綁定機制,而C++是編譯時靜態(tài)綁定柱搜,并通過嵌入類和虛函數(shù)來模擬實現(xiàn)迟郎;
  3. OC在編譯階段降低了編譯要求提高了靈活性,而C++則是提高了編譯要求聪蘸,在編譯過程中就發(fā)現(xiàn)更多的潛在錯誤宪肖,在運行前改正,降低了靈活性健爬;

以下面的代碼為例控乾,在編譯期間,C++認為是錯誤的娜遵,而OC則認為沒有問題:

NSString *test =(id) [[NSArray alloc] init];

OCC++在使用細節(jié)上的不同如下:

  1. 定型:OC是動態(tài)定型蜕衡,可以允許根據(jù)字符串名字來訪問方法和類,還可以動態(tài)鏈接和添加類设拟;
  2. 繼承:OC不支持多繼承慨仿,C++支持多繼承久脯;
  3. 函數(shù)調(diào)用:OC通過消息傳遞實現(xiàn)函數(shù)調(diào)用,而C++直接進行函數(shù)調(diào)用镰吆;
  4. 接口:OC采用Protocol形式來定義接口帘撰,而C++采用虛函數(shù)形式來定義接口;
  5. 重載:OC不允許同一個類中兩個方法有相同的名字(即使只是參數(shù)類型不同)万皿,但C++可以摧找;

二、編譯型語言與解釋型語言

Objective-C屬于編譯型語言牢硅,這是為了保證iPhone的執(zhí)行效率蹬耘;

1.編譯型語言
  1. 程序運行前,必須先通過編譯器生成機器碼唤衫,機器碼直接通過CPU執(zhí)行婆赠,運行時不需要重新翻譯;
  2. 程序執(zhí)行效率高佳励,但依賴編譯器休里,調(diào)試周期長、跨平臺性差些赃承;
  3. 代表語言:C妙黍、C++OC等瞧剖;
2.解釋型語言
  1. 程序運行前拭嫁,不需要進行編譯,而是以文本方式存儲程序代碼抓于,運行時需要解釋器解釋后再運行做粤;
  2. 程序執(zhí)行效率低下,但是程序具有動態(tài)性捉撮,運行后也可以隨時增加和更新代碼來改變程序邏輯怕品;
  3. 代表語言:JavascriptPython等巾遭;
編譯原理-語言的分類

三肉康、編譯器LLVM與CLang

1.編譯器

概念:把一種編程語言(原始語言)轉(zhuǎn)換為另一種編程語言(目標語言)的程序;

大多數(shù)編譯器都分前端后端兩部分:

  • 前端:負責詞法分析語法分析灼舍、生成中間代碼吼和;
  • 后端:以中間代碼作為輸入,進行與架構(gòu)無關(guān)的代碼優(yōu)化骑素,接著針對不同架構(gòu)生成不同的機器碼炫乓;

補充:

  1. 前后端以中間代碼作為媒介,使得前后端可以獨立的變化,互不影響厢岂;
  2. 這樣的好處在于:新增一門語言只需要修改前端光督,而新增一種CPU架構(gòu)只需要修改后端即可阳距;
2.LLVM與Clang

LLVM是蘋果當前使用的編譯器:

  1. LLVM是一套編譯器基礎(chǔ)設(shè)施項目塔粒,為自由軟件,以C++寫成筐摘,包含一系列模塊化的編譯器組件和工具鏈卒茬,用來開發(fā)編譯器前端后端
  2. 基于 LLVM 衍生出了一些強大的子項目咖熟,比如:ClangLLDB圃酵。

CLang基于LLVM,是一個高度模塊化開發(fā)的輕量級編譯器馍管;

  1. CLang主要來自蘋果電腦的支持郭赐,同時支持C芍阎、Objective-C以及C++刺下;
  2. CLang用于替代Xcode5版本前使用的GCC鉴未,編譯速度提高了3倍:
3.理解iOS中的編譯器
  1. iOS開發(fā)中饶氏,通常LLVM被認為是編譯器的后端述寡,而Clang是作為編譯器的前端鬼雀;
  2. 二者以 IR(中間代碼)作為媒介实撒,這樣前后端分離靴患,使得前后端可以獨立的變化桨菜,互不影響豁状;
  3. C 語言家族的前端是 clangswift 的前端是 swiftc倒得,但二者的后端都是 LLVM泻红;

四、理解iOS編譯流程

1.編譯流程圖

LLVM的編譯過程相當復雜霞掺,iOS代碼運行需要經(jīng)過:預處理谊路、編譯匯編根悼、鏈接四個關(guān)鍵階段凶异,具體的流程如下圖:

編譯原理-編譯流程
2.準備測試文件

OC語言為例,詳細分析代碼的編譯流程挤巡,準備一個main.m文件的內(nèi)容如下:

#import <Foundation/Foundation.h>
/// 增加注釋:宏定義Name
#define Name "梧雨北辰"
int main(int argc, const char * argv[]) {
    NSLog(@"Hello, %s", Name);
    return 0;
}

五剩彬、預處理(Prepressing)

1.主要功能
  1. 替換宏:替換代碼中各種宏定義,如定義的常量矿卑、函數(shù)等喉恋;
  2. 導入頭文件:將#include包含的文件插入到該指令位置等;
  3. 清理注釋:刪除所有注釋:///* */等轻黑;
  4. 條件編譯:處理#if糊肤、#ifdef#endif等類似的條件編譯氓鄙;
  5. 添加行號和文件名標識:以便于編譯時編譯器能夠顯示警告和錯誤的所在行號馆揉;
2.查看預處理結(jié)果

使用xcrun命令,在終端執(zhí)行預處理操作:

xcrun clang -E main.m

終端顯示效果如下:

# 1 "main.m"
# 1 "<built-in>" 1

...

# 1 "/Applications/Xcode13.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Headers/FoundationLegacySwiftCompatibility.h" 1 3
# 193 "/Applications/Xcode13.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h" 2 3
# 2 "main.m" 2


int main(int argc, const char * argv[]) {
 NSLog(@"Hello, %s", "梧雨北辰");
 return 0;
}

結(jié)果分析:

  1. 預處理后的文件中抖拦,注釋已經(jīng)被清理升酣,宏定義也已經(jīng)被替換;
  2. 預處理后的文件有很多行态罪,因為該過程中導入了頭文件(Foundation.h)噩茄,而且這個過程是遞歸的;

六复颈、編譯(Compilation)

1. 詞法分析(Lexical Analysis)

主要功能:通過掃描器绩聘,分割識別源代碼符號(如大小括號、=耗啦、字符串)凿菩;

使用xcrun命令,在終端執(zhí)行詞法分析操作:

xcrun clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m

終端顯示效果如下:

annot_module_include '#import <Foundation/Foundation.h>
/'      Loc=<main.m:1:1>
int 'int'    [StartOfLine]  Loc=<main.m:4:1>
identifier 'main'    [LeadingSpace]

......

r_brace '}'  [StartOfLine]  Loc=<main.m:7:1>
eof ''      Loc=<main.m:10:1>

結(jié)果分析:

  1. 每個被分割的源代碼符號都被記錄了位置芹彬,方便后續(xù)定位錯誤蓄髓;
  2. 比如Loc=<main.m:4:1> 就表示:'int'這個符號是從源文件main.m的第4行的第1個字符開始的;
2.語法分析(Semantic Analysis)

主要功能:對源代碼符號進行分析舒帮,驗證語法是否正確会喝,最后生成AST語法樹;

使用xcrun命令玩郊,查看語法分析結(jié)果:

xcrun clang -fsyntax-only -Xclang -ast-dump main.c | open -f

AST語法樹:

  1. 是抽象語法樹肢执,結(jié)構(gòu)上比代碼更精簡,遍歷速度更快译红;
  2. 能夠更快的進行靜態(tài)檢查预茄,同時生成IR(中間代碼);
3.靜態(tài)分析(Static Analysis)

主要功能:對AST樹進行遍歷分析侦厚,包括類型檢查耻陕、方法實現(xiàn)檢查,會及時提示錯誤刨沦;

4.生成中間代碼(Code Generation)

主要功能:CodeGen負責將AST語法樹自頂向下遍歷诗宣,逐步翻譯成IR中間代碼;

IR中間代碼:

  1. 這是一種更接近于機器碼的語言想诅,使得編譯器被分為前端和后端召庞,不同的平臺可以利用各自的編譯器將中間代碼岛心,轉(zhuǎn)化為適合不同平臺的機器碼;
  2. 對于iOS系統(tǒng)來說篮灼,IR中間代碼生成的就是Mach-O可執(zhí)行文件;
  3. IR是前端的輸出忘古,后端的輸入;

七诅诱、匯編(Assembly)

輸出中間代碼標志著前端工作的完成髓堪,接下來將進入后端的處理流程。

1.LLVM優(yōu)化中間代碼

中間代碼IR進入后端逢艘,LLVM會對其進行優(yōu)化:

  1. Optimization Level
  2. bitcode
2.生成匯編代碼

LLVMIR進行優(yōu)化后旦袋,會針對不同架構(gòu)生成不同匯編代碼骤菠;

匯編階段的目的:

  1. 將代碼匯編化它改,并將符號進行歸類;
  2. 將外部導入符號商乎,放到重定位符號表央拖;
  3. 最后生成一個或多個.o目標文件;

使用xcrun命令鹉戚,生成匯編文件:

xcrun clang -S main.m -o main.s

打開.s文件鲜戒,摘取內(nèi)容如下:

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 11, 0 sdk_version 11, 3
    .globl  _main                           ## -- Begin function main
    
    // ......

    callq   _NSLog

    // ......
.subsections_via_symbols

可以看到,匯編文件中的NSLog操作已經(jīng)被轉(zhuǎn)化為匯編命令形式的調(diào)用抹凳,即callq _NSLog遏餐;

3.生成目標文件

該階段是匯編器匯編代碼轉(zhuǎn)換為機器代碼,并輸出目標文件赢底,即.o文件失都;

使用xcrun命令,生成目標文件:

xcrun clang -fmodules -c main.m -o main.o

使用file命令幸冻,查看目標文件類型:

% file main.o
main.o: Mach-O 64-bit object x86_64

可以看到粹庞,匯編器生成Mach-O格式的文件,而且是object類型洽损,即目標文件類型:

  1. Mach-O文件是用于iOSOS平臺上的文件類型庞溜;
  2. Mach-O作為a.out格式的替代,提供了更強的擴展性碑定,也提升了符號表中信息的訪問速度流码;

使用xcrun命令,查看下main.o中的符號:

xcrun nm -nm main.o

終端顯示效果如下:

                 (undefined) external _NSLog
                 (undefined) external ___CFConstantStringClassReference
0000000000000000 (__TEXT,__text) external _main

可以看到延刘,此時我們使用的NSLog函數(shù)漫试,對應(yīng)著_NSLog符號:

  1. undefined:表示在當前文件暫時找不到符號_NSLog
  2. external:表示這個符號是外部可以訪問的访娶,對應(yīng)表示文件私有的符號是non-external商虐;

八觉阅、鏈接(Linking)

主要功能:符號解析、重定位秘车、合并目標文件典勇,最終生成可執(zhí)行文件;

1.使用xcrun命令執(zhí)行鏈接叮趴,得到可執(zhí)行文件
xcrun clang main.o -o main
2.使用file命令割笙,查看文件類型
% file main
main: Mach-O 64-bit executable x86_64
% ./main
2021-10-01 19:06:41.846 main[5663:660299] Hello, 梧雨北辰

結(jié)果分析:雖然還是Mach-O格式,但此時已經(jīng)是executable類型了眯亦,即可執(zhí)行文件伤溉。而且運行該文件后也打印出了預期的結(jié)果;

3.再次使用xcrun命令妻率,查看可執(zhí)行文件的符號表
% xcrun nm -nm main
                 (undefined) external _NSLog (from Foundation)
                 (undefined) external ___CFConstantStringClassReference (from CoreFoundation)
                 (undefined) external dyld_stub_binder (from libSystem)
0000000100000000 (__TEXT,__text) [referenced dynamically] external __mh_execute_header
0000000100003f40 (__TEXT,__text) external _main
0000000100008008 (__DATA,__data) non-external __dyld_private

結(jié)果分析:_NSLog符號依然是undefined乱顾,不過此時多了一些信息,即from Foundation宫静,表示這個符號來自于Foundation走净,會在運行時動態(tài)綁定;

4.鏈接階段的主要任務(wù)

1.符號解析

將每個符號引用和對應(yīng)的符號定義關(guān)聯(lián)起來孤里;

  • 鏈接器鏈接多文件時會創(chuàng)建符號表伏伯,用于記錄所有已經(jīng)定義和未定義的符號;
    1. 出現(xiàn)相同符號捌袜,會報錯:"ld:dumplicate symbols"说搅;
    2. 在其他目標文件里沒有找到到符號,會報錯:"Undefined symbols"虏等;
  • 另外弄唧,鏈接器在整理函數(shù)的符號調(diào)用關(guān)系時,可以幫助我們理清那些函數(shù)沒有被調(diào)用博其,并自動去除掉套才;

2.重定位

將變量名、函數(shù)名這些符號定義與一個內(nèi)存位置關(guān)聯(lián)起來慕淡;

  • 因為只有通過了綁定背伴,機器才知道需要操作什么內(nèi)存地址;
  • 否則峰髓,我們就需要在寫代碼時給每個指令設(shè)置好內(nèi)存地址傻寂,不僅操作繁瑣,而且容易引起出錯携兵;

3.合并目標文件

將多個.m文件編譯產(chǎn)生的.o目標文件與其他Mach-O文件(如dylib疾掰、atbd)徐紧,合成一個Mach-O格式的可執(zhí)行文件静檬;

  • 通常項目都會包含多個文件炭懊,不同文件之間的變量接口函數(shù)就會產(chǎn)生相互依賴關(guān)系;
  • 程序運行前拂檩,需要使用鏈接器將多個文件里的符號和地址綁定起來侮腹,才能保證整個程序里的變量、接口的正常調(diào)用稻励;
5.理解靜態(tài)鏈接與動態(tài)鏈接

靜態(tài)鏈接:作用于編譯期父阻,鏈接后的文件依然可能會存在一些"undefined"的符號。但是這些符號都會被記錄下來望抽,在運行時再通過dlopendlsym動態(tài)鏈接綁定加矛;

動態(tài)鏈接:作用于運行時,這樣的優(yōu)勢在于:諸多類似UIKit這樣的共享庫將不必包含在每一個App包里煤篙。比如:我們使用到的UIKit系統(tǒng)庫斟览,等到點擊App真正開始運行之前,才會去鏈接依賴的UIKit舰蟆,鏈接完成再運行App趣惠;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者身害。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市草戈,隨后出現(xiàn)的幾起案子塌鸯,更是在濱河造成了極大的恐慌,老刑警劉巖唐片,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丙猬,死亡現(xiàn)場離奇詭異,居然都是意外死亡费韭,警方通過查閱死者的電腦和手機茧球,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來星持,“玉大人抢埋,你說我怎么就攤上這事《皆荩” “怎么了揪垄?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長逻翁。 經(jīng)常有香客問我饥努,道長,這世上最難降的妖魔是什么八回? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任酷愧,我火速辦了婚禮驾诈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘溶浴。我一直安慰自己翘鸭,他們只是感情好,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布戳葵。 她就那樣靜靜地躺著就乓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拱烁。 梳的紋絲不亂的頭發(fā)上生蚁,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機與錄音戏自,去河邊找鬼邦投。 笑死,一個胖子當著我的面吹牛擅笔,可吹牛的內(nèi)容都是我干的志衣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼猛们,長吁一口氣:“原來是場噩夢啊……” “哼念脯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起弯淘,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绿店,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后庐橙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體假勿,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年态鳖,在試婚紗的時候發(fā)現(xiàn)自己被綠了转培。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡浆竭,死狀恐怖浸须,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兆蕉,我是刑警寧澤羽戒,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站虎韵,受9級特大地震影響易稠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜包蓝,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一驶社、第九天 我趴在偏房一處隱蔽的房頂上張望企量。 院中可真熱鬧,春花似錦亡电、人聲如沸届巩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恕汇。三九已至,卻和暖如春或辖,著一層夾襖步出監(jiān)牢的瞬間瘾英,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工颂暇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缺谴,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓耳鸯,卻偏偏與公主長得像湿蛔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子县爬,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

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