#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 Settings
和 TARGETS -> Build Settings
中 都包含了 Search Paths
這一欄,Xcode 將上面的命令行選項可視化的展示給我們;
PROJECT 中的設置默認不被 TARGETS 繼承,只有當 TARGETS 的設置中加入了 $(inherited)
時,才被繼承。
多個 -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:
編譯
ViewController.m
:
最終輸出:
//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:
編譯 ViewController.m
:
最終輸出:
//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:
編譯 ViewController.m
:
最終輸出:
//Build2[] MAXFLOAT: 0.300000
- 指定為
-I
選項的目錄掃描順序在-isystem
之前。
-isysroot, -iquote
-isysroot <dir>
set the system root directory (usually /). dir/user/include/
Search Paths:
編譯 ViewController.m
:
最終輸出:
//Build2[] MAXFLOAT: 340282346638528859811704183484516925440.000000
- 指定為
-iqoute
選項的目錄不影響#import <>
。
此處如果將 Always Search User Paths
設為 YES:
那么 /directory4
目錄將會被指定為 -I
:
所以最終輸出就會變成:
//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:
編譯 ViewController.m
:
最終輸出:
// Build[] MyClass.h: directory/MyClass.h
- 首先在代碼所在文件的目錄下進行搜索
-iquote
目錄結構:
/Build/ -> /directory/ -> ViewController.h
-> /directory2/ -> MyClass.h
-> /directory3/ -> MyClass.h
先將 /directory
目錄下的 MyClass.h
文件移除。
Search Paths:
編譯 ViewController.m
:
最終輸出:
// Build[] MyClass.h: directory4/MyClass.h
-iquote
的目錄掃描順序在-I
之前
@import
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 inUser 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 inUser 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.
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