clang是如何查找頭文件的

我們知道,如果在項目中存在兩個同名的類文件(即使所處文件夾不同),會報錯“duplicate symbols”。但是如果在不同的文件夾下創(chuàng)建同一個類的擴展并且擴展名相同卻是可以的。

這是因為編譯器不允許我們在同一個項目中創(chuàng)建兩個同名的類,編譯時就會主動檢查。而對于Category,編譯器并不關(guān)心是否存在同名擴展或同名方法,Category檢查是放在運行期的。Category的使用依賴于開發(fā)者按約定來自我規(guī)范從而避免造成一些奇怪的問題,一般建議Category中的方法加上前綴和下劃線來避免方法重復(fù)。

如果我們項目中不小心造成了存在多個同名的擴展會怎樣呢?

Duplicate Category

其中①處的擴展有+log方法,②處的擴展有+log+upload方法。

使用的時候卻發(fā)現(xiàn)會提示找不到upload方法:

#import "NSObject+Log.h"

- (void)viewDidLoad {
    [super viewDidLoad];
    [NSObject log];//找得到方法
    [NSObject upload];//找不到方法
}

是不是編譯順序?qū)е碌哪??在Build phases -> Compile Sources中修改兩個同名文件的順序后發(fā)現(xiàn)還是找不到upload方法。

如果把import語句修改為#import "AnotherLog/NSObject+Log.h"就可以改變實際引入的擴展為②處的擴展,就能找到upload方法了。

那么#import "NSObject+Log.h"到底是怎么查找目標文件的呢?

#import <file>用于引入系統(tǒng)頭文件,它在一個標準文件系統(tǒng)目錄列表中尋找名為file的文件。你可以用-I選項在預(yù)處理階段往目錄列表前面添加其他目錄(對應(yīng)Xcode中配置Header Search Paths中添加的目錄,編譯時就會自動在前面加上-I選項)。
#importt "file"用于引入用戶頭文件,它首先在包含當前文件的目錄下尋找file文件,如果找不到,接著在引用目錄下尋找,如果還是找不到,使用與#import <file>方式相同的查找方式尋找。你可以用-iquote選項在預(yù)處理階段往引用目錄列表前面添加其他目錄(對應(yīng)Xcode中設(shè)置Use Header Maps為NO以及配置User Header Search Paths)。

#import <file> 在 Always Search User Paths 為YES的情況下,User Header Search Paths 中目錄的掃描順序排在 Header Search Paths 之前。而 #import "file" 無論 Always Search User Paths 是否 YES,都是如此。

比如有這樣的目錄結(jié)構(gòu):

目錄結(jié)構(gòu)

三個目錄下分別定義同名的頭文件TestClass.h,每個頭文件中都定義一個同名字符串dir,但具體的值不同:static NSString *dir = @"directory/TestClass.h";。在Header Search Paths中添加三個目錄:

Header Search Paths

在ViewController中添加代碼:

#import "ViewController.h"
#import <TestClass.h>

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"TestClass.h at: %@", dir);
}
@end

這里會輸出:TestClass.h at: directory3/TestClass.h。是因為在Header Search Paths中,我們把directory3放在了第一位。

如果我們在Xcode中選中ViewController.m,然后執(zhí)行Product -> Perform Action -> Preprocess "ViewController.m" 會看到如下結(jié)果:

Xcode_preprocess_viewcontroller

因為我們import命令在編譯時把引用的文件插入到import所在的位置。說明編譯器找到了directory3下的TestClass文件。

編譯后,在Report Navigator中可以看到在編譯ViewController時會自動在添加的目錄前面加上-I選項:

編譯-I選項

如果把build后編譯ViewController的指令拷貝出來放在終端中,在后面加上-v選項來打印詳細信息,會看到clang是如何查找目標文件的:

如果改為#import "TestClass.h",則輸出:TestClass.h at: directory/TestClass.h,因為在包含當前的目錄下找到了TestClass文件:

#import "ViewController.h"
#import "TestClass.h"


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"TestClass.h at: %@", dir);
}
@end

如果把當前目錄下的TestClass文件重命名,然后設(shè)置Use Header Maps為NO,把directory3目錄添加到User Header Search Paths中,此時會輸出TestClass.h at: directory3/TestClass.h。因為這個時候在包含當前文件的目錄下找不到TestClass文件,就在引用目錄下查找,就找到了directory3中。

此時再看編譯ViewController時,會發(fā)現(xiàn)編譯器在directory3的前面加上了-iquote選項:

-iquote選項

另外,如果在System Header Search Paths中添加了目錄,那么在編譯時會自動在目錄前面加上-isystem選項,指定-isystem選項的目錄會優(yōu)先于系統(tǒng)目錄前被查找。-isysroot選項指定系統(tǒng)目錄。

其他概念:

Use Header Maps:這個選項開啟后,會為編譯器提供一份文本的文件名和相對路徑的映射(.hmap文件),我們可以直接引用工程中的文件,而不需要在 Header Search Path 中配置。選項關(guān)閉后,你需要把每一個包含頭文件的路徑添加到目標的header search paths中。

Framework/Library Search Paths:
1、Framework Search Paths
附加到項目中的framework(.framework bundles)的搜索路徑,在iOS開發(fā)中使用的不是特別多,通常對于iOS的開發(fā)來說一般使用系統(tǒng)內(nèi)置的framework。
2、Library Search Paths
附加到項目中的第三方Library(.a files)的搜索路徑,Xcode會自動設(shè)置拖拽到Xcode中的.a文件的路徑,為了便于移植或者是多人協(xié)作開發(fā)一般會手動設(shè)置。
比如對于設(shè)置百度的地圖的SDK,我們會設(shè)置如下:
$(SRCROOT)/../libs/Release$(EFFECTIVE_PLATFORM_NAME),其中$(SRCROOT)宏代表您的工程文件目錄,$(EFFECTIVE_PLATFORM_NAME)宏代表當前配置是OS還是simulator。

Header Search Path:
1、C/C++頭文件引用
在C/C++中,include是變異指令,在編譯時,編譯器會將相對路徑替換成絕對路徑,因此,頭文件的絕對路徑等同于搜索路徑+相對路徑。
(1)#include <iostream.h>:引用編譯器的類庫路徑下的頭文件
(2)#include "hello.h":引用工程目錄的相對路徑的頭文件
2、(User) Header Search Path
(1)Header Search Path指的是頭文件的搜索路徑。
(2)User Header Search Paths指的是用戶自定義的頭文件的搜索路徑
3、Always Search User Paths(廢棄了)
如果設(shè)置了Always Search User Paths為YES,編譯器會優(yōu)先搜索User Header Search Paths配置的路徑,在這種情況下#include <string.h>,User Header Search Paths搜索目錄下面的文件會覆蓋系統(tǒng)的頭文件。

$(PODS_ROOT)
$(SRCROOT)代表的是項目根目錄下
$(PROJECT_DIR)代表的是整個項目
/../上級目錄
$(inherited) (target在設(shè)置自己路徑的時候如果加了這個,那么就是繼承project里設(shè)置的路徑,默認不繼承。)

參考:

Build settings reference官方解釋
iOS開發(fā)中的Search Paths設(shè)置
Objective-C 中的 import 和 Search Paths
2.1 Include Syntax

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 開發(fā)的過程當中,導(dǎo)入第三方庫(framework/.a)或者下載使用別人的demo會經(jīng)常會遇到一些關(guān)于庫的導(dǎo)入的問...
    lhg_serven閱讀 3,992評論 0 8
  • #include #include 是預(yù)處理指令,在編譯之前的預(yù)處理期執(zhí)行。它的作用是將引入文件中的內(nèi)容拷貝到當前...
    寒咯閱讀 3,712評論 0 10
  • 官網(wǎng) 中文版本 好的網(wǎng)站 Content-type: text/htmlBASH Section: User ...
    不排版閱讀 4,695評論 0 5
  • 模塊化工作中,會指定庫與庫之間的依賴關(guān)系,根據(jù)依賴關(guān)系分層,但隨著開發(fā)進行,依賴關(guān)系又慢慢被破壞。如何讓后續(xù)的開發(fā)...
    donghuan1閱讀 4,005評論 0 3
  • 巴山蜀水淚難停, 折翼精靈夢未成。 魂斷藍天金孔雀, 丹心不改碧空情。
    新平_4494閱讀 279評論 4 6

友情鏈接更多精彩內(nèi)容