#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