1、編譯
OC屬于高級語言,需要翻譯成計算機(jī)可以識別的機(jī)器碼,所以就需要用到了編譯
- 編譯過程:
源文件(.h .m .cpp)--> 預(yù)編譯期 --> 編譯期(詞法分析、語法分析、靜態(tài)分
析)--> 生成中間代碼和優(yōu)化 --> 匯編 --> 靜態(tài)鏈接(ldb)
- 編譯器
- LLVM項目是模塊化、可重用的編譯器以及工具鏈技術(shù)的集合
- LLVM開始成長之后,成為眾多編譯工具及低級工具技術(shù)的統(tǒng)稱,使得這個名字變得更不貼切,開發(fā)者因而決定放棄這個縮寫的意涵,現(xiàn)今LLVM已單純成為一個品牌,適用于LLVM下的所有項目,包含LLVM中介碼(LLVM IR)、LLVM除錯工具、LLVM C++標(biāo)準(zhǔn)庫等
- Xcode就是采用LLVM作為默認(rèn)的編譯器
編譯的三段式設(shè)計:前端 -- 優(yōu)化器 -- 后端- 前端,對源碼做詞法分析、語法分析、語義分析、生成中間代碼
- 優(yōu)化器,用于中間代碼優(yōu)化
- 后端,用于生成機(jī)器碼
- LLVM 架構(gòu)

- 廣義的LLVM:整個LLVM架構(gòu);狹義的LLVM:LLVM后端(代碼優(yōu)化、目標(biāo)代碼生成等)
- 前端負(fù)責(zé)生成中間代碼也就是bitcode,LLVM下,不同的語言有不同的編譯器前端,OC的是clang
- 不同的前后端優(yōu)化使用統(tǒng)一的中間代碼LLVM IR(Intermediate Representation),對bitcode進(jìn)行各種類型的優(yōu)化,將bitcode代碼進(jìn)行一些邏輯的轉(zhuǎn)換,使得代碼效率更高,體積更小
- 后端,也叫CodeGenerator,負(fù)責(zé)把優(yōu)化后的bitcode編譯為指定目標(biāo)架構(gòu)的機(jī)器碼,比如 X86 Backend負(fù)責(zé)把bitcode編譯為x86指令集的機(jī)器碼
- 三段式的優(yōu)勢,LLVM體系中,不同語言源代碼將會被轉(zhuǎn)化為統(tǒng)一的bitcode格式,三個模塊相互獨(dú)立,可以充分復(fù)用。比如,如果開發(fā)一門新的語言,只要制造一個該語言的前端,將源碼編譯為bitcode,優(yōu)化和后端不用管。同理,如果新的芯片架構(gòu)問世,只需基于LLVM重新編寫一套目標(biāo)平臺的后端即可
Clang
- Clang 是一個由Apple主導(dǎo)編寫,基于LLVM的C/C++/Objective-C輕量級編譯器。源代碼發(fā)布于LLVM BSD協(xié)議下。 Clang將支持其普通lambda表達(dá)式、返回類型的簡化處理以及更好的處理constexpr關(guān)鍵字。
- 它與GNU C語言規(guī)范幾乎完全兼容(當(dāng)然,也有部分不兼容的內(nèi)容, 包括編譯命令選項也會有點(diǎn)差異),并在此基礎(chǔ)上增加了額外的語法特性,比如C函數(shù)重載 (通過attribute((overloadable))來修飾函數(shù)),其目標(biāo)(之一)就是超越GCC。
- LLVM項目的一個子項目
- 基于LLVM的OC/C/C++/OC++的編譯器前端
- 編譯速度更快
- 內(nèi)存占用小,Clang生成的AST所占用的內(nèi)存是GCC的五分之一左右
- 模塊化設(shè)計:Clang采用基于庫的模塊化設(shè)計,易于IDE集成及其他用途的重用
- 診斷信息可讀性強(qiáng):在編譯過程中,Clang創(chuàng)建并保留了大量詳細(xì)的元數(shù)據(jù)(metadata),有利于調(diào)試和錯誤報告
- 設(shè)計清晰簡單,容易理解,易于擴(kuò)展增強(qiáng)
客觀的說GCC也有很多優(yōu)點(diǎn):例如支持多平臺,基于C無需 C++編譯器即可編譯。這個優(yōu)點(diǎn)到蘋果那里反而成了缺點(diǎn),蘋果需要的是快。
//1、將 main.m 編譯成 main.cpp
clang -rewrite-objc main.m -o main.cpp
//2、將 ViewController.m 編譯成 ViewController.cpp
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot / /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.7.sdk ViewController.m
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk ViewController.m
//以下兩種方式是通過指定架構(gòu)模式的命令行,使用xcode工具 xcrun
//3、模擬器文件編譯
- xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
//4、真機(jī)文件編譯
- xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp
編譯過程

- 預(yù)處理會替進(jìn)行頭文件引入,宏替換,注釋處理,條件編譯(#ifdef)等操作。
- Clang前端負(fù)責(zé)分析源代碼語法分析,語義分析,并構(gòu)建針對該語言的抽象語法樹(AST)
- 詞法分析器讀入源文件的字符流,將他們組織稱有意義的詞素(lexeme)序列,對于每個詞素,此法分析器產(chǎn)生詞法單元(token)作為輸出
- 語法分析,詞法分析的Token流會被解析成一顆抽象語法樹(abstract syntax tree - AST)
- AST 是抽象語法樹,結(jié)構(gòu)上比代碼更精簡,遍歷起來更快,所以使用 AST 能夠更快速地進(jìn)行靜態(tài)檢查,同時還能更快地生成 LLVM IR(中間代碼)
- 最后 AST 會生成 LLVM IR,LLVM IR 是一種更接近機(jī)器碼的語言,區(qū)別在于和平臺無關(guān),通過 IR 可以生成多份適合不同平臺的機(jī)器碼。對于 iOS 系統(tǒng),IR 生成的可執(zhí)行文件就是 Mach-O
- 優(yōu)化器統(tǒng)一對前端生成的中間代碼LLVM IR進(jìn)行代碼優(yōu)化
- LLVM 對中間代碼IR進(jìn)行優(yōu)化,并針對不同架構(gòu)生成目標(biāo)文件,以匯編代碼的形式進(jìn)行輸出
- 匯編階段,將上一步生成的匯編代碼轉(zhuǎn)化成機(jī)器代碼,最終以.o文件進(jìn)行輸出
- 鏈接器ldb(靜態(tài)鏈接),將匯編生成的.o文件和(dylib、.a、tbd)文件進(jìn)行鏈接生成可執(zhí)行文件(Mac-O)
預(yù)編譯
預(yù)編譯過程主要處理源代碼文件中以#開始的預(yù)編譯指令
- 將所有的 “#define”刪除,并且展開所有的宏定義
- 處理所有條件預(yù)編譯指令,比如“#if”、“#ifder”, “#elif”, “#else”,
"#endif" - 處理 “#include”預(yù)編譯指令,將被包含的文件插入到該預(yù)編譯指令的位置。注意,這個過程是遞歸進(jìn)行的,也就是說被包含的文件可能還包含其他文件。
- 刪除所有的注釋
//和/**/, - 添加行號和文件名標(biāo)識,比如#2“hello.c”2,以便于編譯時編譯器產(chǎn)生調(diào)試用的行號
信息及用于編譯時產(chǎn)生編澤錯誤或警告時能夠顯示行號。 - 保留所有的
#pragma編譯器指令,因為編譯器須要使用它們。
經(jīng)過預(yù)編譯后的.i文件不包含任何宏定義,因為所有的宏己經(jīng)被展開,并且包含的文件
也已經(jīng)被插入到.i文件中。所以當(dāng)我們無法判斷宏定義是否正確或頭文件包含是否正確時,可以查看預(yù)編譯后的文件來確定問題。
- 預(yù)編譯指令
宏定義:
1.不含參數(shù): #define MaxF 1
2.含有參數(shù):#define SUMM(a,b) a+b
宏定義說明:
1.宏定義是用宏名代替一個字符串,只作簡單置換,不作正確性檢查,同時也不會做運(yùn)算邏輯處理,同時在進(jìn)行宏定義時,可以引用已定義的宏名,可以層層置換。(在這里需要特別注意的是:當(dāng)宏涉運(yùn)算時,要根據(jù)情況來添加括號,防止運(yùn)算邏輯出現(xiàn)錯誤,#define SUMM(a,b) a+b,在引用SUMM進(jìn)行運(yùn)算時容易出錯
2.宏定義不是C語句,不必在行末加分號。如果加了分號則會連分號一起進(jìn)行置換
3.宏定義是專門用于預(yù)處理命令的一個專用名詞,它與定義變量的含義不同,只作字符替換,不分配內(nèi)存空間
4.#define命令出現(xiàn)在程序中函數(shù)的外面,宏名的有效范圍為定義命令之后到本文件結(jié)束。通常#define命令寫在文件開頭,函數(shù)之前,作為文件一部分,在此文件范圍內(nèi)有效
- 條件編譯
條件編譯就是在編譯之前預(yù)處理器根據(jù)預(yù)處理指令判斷對應(yīng)的條件,如果條件滿足就將對應(yīng)的代碼編譯進(jìn)去,否則代碼就根本不進(jìn)入編譯環(huán)節(jié)(相當(dāng)于根本就沒有這段代碼)
1.#if編譯預(yù)處理中的條件命令, 相當(dāng)于C語法中的if語句
2.#ifdef判斷某個宏是否被定義, 若已定義, 執(zhí)行隨后的語句
3.#ifndef與#ifdef相反, 判斷某個宏是否未被定義
4.#elif若#if,#ifdef,#ifndef或前面的#elif條件不滿足, 則執(zhí)行#elif之后的語句, 相當(dāng)于C語法中的else-if
6.#else與#if,#ifdef,#ifndef對應(yīng), 若這些條件不滿足, 則執(zhí)行#else之后的語句, 相當(dāng)于C語法中的else
7.#endif#if,#ifdef,#ifndef這些條件命令的結(jié)束標(biāo)志.
8.#if與#ifdef的區(qū)別:#if是判斷后面的條件語句是否成立,#ifdef是判斷某個宏是否被定義過
#ifdef MAX_F
// 如果定義了宏MAX_F,則編譯此處的代碼
#else
// 如果沒有定義宏MAX_F,則編譯此處的代碼
#endif
// 同樣
#ifndef MAX_F
// 如果沒有定義宏MAX_F,則編譯此處的代碼
#elif MAX_INT
// 如果定義了宏MAX_F,同時還定義了宏MAX_INT,則編譯此處的代碼
#else
// 定義了宏MAX_F,但是沒有定義宏MAX_INT,則編譯此處的代碼
#endif
為了防止該頭文件被引用時發(fā)生重復(fù)引用
#ifndef Header_h
#define Header_h
#endif
- 文件包含
C語言下一般使用
#include, OC中一般使用#import,它們的區(qū)別是:在使用#include的時候要注意處理重復(fù)引用,#import大部分功能和#include是一樣的,但是他處理了重復(fù)引用的問題,我們在引用文件的時候不用再去自己進(jìn)行重復(fù)引用處理。OC中還有一個引用聲明 @class主要是用于聲明一個類,告訴編譯器它后面的名字是一個類的名字,而這個類的定義實現(xiàn)是暫時不用知道的。一般來說,在interface中(.h文件)引用一個類,就用@class,它會把這個類作為一個類型來使用,而在實現(xiàn)這個interface的文件中,如果需要引用這個類的實體變量或者方法之類的,還是需要import這個在@class中聲明的類
@class僅僅是告訴編譯器有這么一個類, 具體這個類里有什么信息, 完全不知
使用include不會檢測之前有沒有對這個頭文件進(jìn)行包含,所以一般都有一個宏控制來防止頭文件被多次包含,不過現(xiàn)在新建頭文件時編譯器都會自動生成一段
而使用import則不必考慮,它會自動檢測所包含的頭文件在之前有沒有被包含,如果已被包含則不再包含。在object-c中一般都是用import。
編譯
編譯器其實就是將高級語言翻譯成機(jī)器語言的過程
匯編
匯編器是將匯編代碼轉(zhuǎn)變成機(jī)器可以執(zhí)行的指令,每—個匯編語句幾乎都對應(yīng)一條機(jī)器
指令。所以匯編器的匯編過程相對于編譯器來講比較簡單,它沒有復(fù)雜的語法,也沒有語義,也不需要做指令優(yōu)化,只是根據(jù)匯編指令和機(jī)器指令的對照表一一翻譯即可,最終生成.o目標(biāo)文件(object file)
知識點(diǎn)擴(kuò)展
'iOS 芯片架構(gòu)指令集'
1、armv7|armv7s|arm64都是ARM處理器的指令集
2、i386|x86_64 是Mac處理器的指令集
3、對應(yīng)設(shè)備:
arm64:iPhone6s | iphone6s plus|iPhone6| iPhone6 plus|iPhone5S | iPad Air| iPad mini2(iPad mini with Retina Display)
armv7s:iPhone5|iPhone5C|iPad4(iPad with Retina Display)
armv7:iPhone4|iPhone4S|iPad|iPad2|iPad3(The New iPad)|iPad mini|iPod Touch 3G|iPod Touch4
i386是針對intel通用微處理器32位處理器
x86_64是針對x86架構(gòu)的64位處理器
模擬器32位處理器測試需要i386架構(gòu),
模擬器64位處理器測試需要x86_64架構(gòu),
真機(jī)32位處理器需要armv7,或者armv7s架構(gòu),
真機(jī)64位處理器需要arm64架構(gòu)