Objective-C 中的 import 和 Search Paths

#include

#include 是預處理指令,在編譯之前的預處理期執行。它的作用是將引入文件中的內容拷貝到當前文件的 #include指令后面,并刪除這一行;

//header.h
char *test (void),

//program.c
int x;
#include "header.h"
int main (void) {
  puts (test ());
}

//編譯器看到的 program.c
int x;
char *test (void);
int main (void) {
  puts (test ());
}

如果兩次 #include 同一個文件,相當于拷貝了兩份相同的頭文件內容,所以編譯時就會報重復定義的錯誤。

// file.m
#include "header.h"
#inclure "header.h"
//duplicate interface definition for class 'header'

這時就需要引入頭文件保護:

#ifndef FILE_FOO_SEEN
#define FILE_FOO_SEEN
//the entire file
#endif

當頭文件再次被引入的時候,條件判斷為 false,因為 FILE_FOO_SEEN 已經被定義過了。預處理器會跳過該文件的全部內容,編譯器編譯時就不會看到兩個重復的引用。

#include 不一定要寫在文件最開頭位置,拷貝內容緊跟在它后面。

#include 后面不一定跟文件名,也可以直接跟路徑(相對路徑/絕對路徑)。

#import

#import 不會造成重復引用,它會自己檢查是否已經引用過,也可以防止遞歸包含。

<> and ""

filename is optionally preceded by a directory specification. The file name must name an existing file. The difference between the two syntax forms is the order in which the preprocessor searches for the type library files when the path is incompletely specified.

也就是說,""<>的區別在于未指定 filename 路徑的情況下,預編譯搜尋路徑的方式。

除了系統目錄外,還可以通過 -I, -isystem, -idirafter, -iquote 選項在預處理階段添加頭文件的搜索路徑,他們之間可以任意選項,任意數量的進行組合,中間以空格符分隔;其中指定為 -iquote 選項的目錄僅適用于 #import "file" 指令,其它選項同時適用兩個指令。

#import <file> 引入頭文件時:

1. 所有指定為 `-I` 選項的目錄從左往右的依次掃描;
2. 所有指定為 `-system` 選項的目錄從左往右依次掃描;
3. 掃描系統目錄;
4. 所有指定為 `-idirafter` 選項的目錄從左往右依次掃描;

#import "file" 引入頭文件時:

1. 首先在當前文件(`#import "file"` 代碼所在文件)的目錄下進行搜索;
2. 所有指定為 `-iquote` 選項的目錄從左往右依次掃描;
3. 所有指定為 `-I` 選項的目錄從左往右的依次掃描;
4. 所有指定為 `-system` 選項的目錄從左往右的依次掃描;
5. 掃描系統目錄;
6. 所有指定為 `-idirafter` 選項的目錄從左往右依次掃描;

Search Paths

PROJECT -> Build SettingsTARGETS -> Build Settings 中 都包含了 Search Paths 這一欄,Xcode 將上面的命令行選項可視化的展示給我們;

image.png

PROJECT 中的設置默認不被 TARGETS 繼承,只有當 TARGETS 的設置中加入了 $(inherited) 時,才被繼承。

Xcode 中的 TARGETS 和 PROJECTS

多個 -I 掃描順序

目錄結構:

/Build/ -> /directory/  -> MyClass.h
                        -> ViewController.h
        -> /directory2/ -> MyClass.h
        -> /directory3/ -> MyClass.h

分別在 /directory, /directory2, /directory3 文件夾下創建相同文件 MyClass.h,文件中都定義了相同變量 static NSString *classKey = @"<dir>/MyClass.h"用來輸出上層目錄名;

#import <MyClass.h>

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

Search Paths:

image.png

編譯 ViewController.m

image.png

最終輸出:

//Build[] MyClass.h: directory3/MyClass.h
  • 指定為 -I 選項的目錄從左往右的依次掃描。

#import <file> 目錄掃描順序

在工程 Build 下,新建 Target Build2

/Build2/ -> /directory/  -> math.h //#define MAXFLOAT    0.1f
                         -> ViewController.h
         -> /directory2/ -> math.h //#define MAXFLOAT    0.2f

/user/include/ -> math.h //#define MAXFLOAT    0x1.fffffep+127f

為了驗證和系統目錄的順序關系,我們將頭文件命名為 math.h,同時分別重定義宏 #MAXFLOAT 的值。

-isystem

Search Paths:

image.png

編譯 ViewController.m

image.png

最終輸出:

//Build2[] MAXFLOAT: 0.200000 
  • 指定為 -isystem 選項的目錄掃描順序在系統路徑之前。

-I, -isystem, current directory

調整目錄結構:

/Build2/ -> /directory/  -> math.h //#define MAXFLOAT    0.1f
                         -> ViewController.h
         -> /directory2/ -> math.h //#define MAXFLOAT    0.2f
         -> /directory3/ -> math.h //#define MAXFLOAT    0.3f
         -> /directory4/ -> math.h //#define MAXFLOAT    0.4f

/user/include/ -> math.h //#define MAXFLOAT    0x1.fffffep+127f

Search Paths:

image.png

編譯 ViewController.m

image.png

最終輸出:

//Build2[] MAXFLOAT: 0.300000 
  • 指定為 -I 選項的目錄掃描順序在 -isystem 之前。

-isysroot, -iquote

-isysroot <dir> set the system root directory (usually /). dir/user/include/

Search Paths:

image.png

編譯 ViewController.m

image.png

最終輸出:

//Build2[] MAXFLOAT: 340282346638528859811704183484516925440.000000 
  • 指定為 -iqoute 選項的目錄不影響 #import <>

此處如果將 Always Search User Paths 設為 YES:

image.png

那么 /directory4 目錄將會被指定為 -I

image.png

所以最終輸出就會變成:

//Build2[] MAXFLOAT: 0.400000
  • #import <file>Always Search User Paths enable 的情況下,User Header Search Paths 中目錄的掃描順序排在 Header Search Paths 之前。而 #import "file" 無論 Always Search User Paths 是否 enable,都是如此。

#import "file" 目錄掃描順序

目錄結構:

/Build/ -> /directory/  -> MyClass.h
                        -> ViewController.h
        -> /directory2/ -> MyClass.h
        -> /directory3/ -> MyClass.h
#import "MyClass.h"

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

current directory

Search Paths:

image.png

編譯 ViewController.m

image.png

最終輸出:

// Build[] MyClass.h: directory/MyClass.h
  • 首先在代碼所在文件的目錄下進行搜索

-iquote

目錄結構:

/Build/ -> /directory/  -> ViewController.h
        -> /directory2/ -> MyClass.h
        -> /directory3/ -> MyClass.h

先將 /directory 目錄下的 MyClass.h 文件移除。

Search Paths:

image.png

編譯 ViewController.m

image.png

最終輸出:

// Build[] MyClass.h: directory4/MyClass.h
  • -iquote 的目錄掃描順序在 -I 之前

@import

iOS里的導入頭文件

iOS - Umbrella Header在framework中的應用

Build Settings

Always Search User Paths

Xcode Help中如下描述:

NOTICE: This setting is deprecated as of Xcode 8.3 and may not be supported in future versions. It is recommended that you disable the setting.

If enabled, both #include <header.h> -style and #include "header.h" -style directives search the paths in User Header Search Paths (USER_HEADER_SEARCH_PATHS) before Header Search Paths (HEADER_SEARCH_PATHS). As a consequence, user headers, such as your own String.h
header, have precedence over system headers when using #include <header.h>. This is done using the -iquote
flag for the paths provided in User Header Search Paths. If disabled and your compiler fully supports separate user paths, user headers are only accessible with #include "header.h" -style preprocessor directives.

For backwards compatibility reasons, this setting is enabled by default. Disabling it is strongly recommended.

Header Search Paths

User Search Header Paths

Use Header Maps

Enable the use of Header Maps, which provide the compiler with a mapping from textual header names to their locations, bypassing the normal compiler header search path mechanisms. This allows source code to include headers from various locations in the file system without needing to update the header search path build settings. --Use Header Maps

Without header maps, you need to add each directory that contains headers to the target’s header search paths. --Header-Map

這個選項開啟后,會為編譯器提供一份文本的文件名和相對路徑的映射,我們可以直接引用工程中的文件,而不需要在 Header Search Path 中配置。

Header-Map Build Settings (.hmap)

Header maps (also known as “header maps”) are files Xcode uses to compile the locations of the headers used in a target. These files use the suffix .hmap. Xcode passes the header maps it puts together to C-based compilers through the -I argument. --Header-Map

參考

The C Preprocessor

Clang Compiler User's Manual

Clang 命令參數

Build Setting Reference

交叉編譯中的 --sysroot 等等在編譯時的作用

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 模塊化工作中,會指定庫與庫之間的依賴關系,根據依賴關系分層,但隨著開發進行,依賴關系又慢慢被破壞。如何讓后續的開發...
    donghuan1閱讀 3,936評論 0 3
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,991評論 19 139
  • PLEASE READ THE FOLLOWING APPLE DEVELOPER PROGRAM LICENSE...
    念念不忘的閱讀 13,551評論 5 6
  • 前幾日圍觀了一場“以小欺大”的舌戰,兩個大男人爭得面紅耳赤,彼此都使出了畢生的洪荒之力,少年此時看來略站上風。其實...
    七琰閱讀 378評論 2 4
  • 今天早上媽媽完成趣配音6-3,寶哥今天起床后發現有一個學校作業漏做了,趕快補做后剩給英語的時間又不多了。跟...
    小舟2017閱讀 277評論 0 0