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 将上面的命令行选项可视化的展示给我们;

《Objective-C 中的 import 和 Search Paths》 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:

《Objective-C 中的 import 和 Search Paths》 image.png

编译
ViewController.m

《Objective-C 中的 import 和 Search Paths》 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:

《Objective-C 中的 import 和 Search Paths》 image.png

编译 ViewController.m

《Objective-C 中的 import 和 Search Paths》 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:

《Objective-C 中的 import 和 Search Paths》 image.png

编译 ViewController.m

《Objective-C 中的 import 和 Search Paths》 image.png

最终输出:

//Build2[] MAXFLOAT: 0.300000 
  • 指定为 -I 选项的目录扫描顺序在 -isystem 之前。

-isysroot, -iquote

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

Search Paths:

《Objective-C 中的 import 和 Search Paths》 image.png

编译 ViewController.m

《Objective-C 中的 import 和 Search Paths》 image.png

最终输出:

//Build2[] MAXFLOAT: 340282346638528859811704183484516925440.000000 
  • 指定为 -iqoute 选项的目录不影响 #import <>

此处如果将 Always Search User Paths 设为 YES:

《Objective-C 中的 import 和 Search Paths》 image.png

那么 /directory4 目录将会被指定为 -I

《Objective-C 中的 import 和 Search Paths》 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:

《Objective-C 中的 import 和 Search Paths》 image.png

编译 ViewController.m

《Objective-C 中的 import 和 Search Paths》 image.png

最终输出:

// Build[] MyClass.h: directory/MyClass.h
  • 首先在代码所在文件的目录下进行搜索

-iquote

目录结构:

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

先将 /directory 目录下的 MyClass.h 文件移除。

Search Paths:

《Objective-C 中的 import 和 Search Paths》 image.png

编译 ViewController.m

《Objective-C 中的 import 和 Search Paths》 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 等等在编译时的作用

    原文作者:寒咯
    原文地址: https://www.jianshu.com/p/75e18591ca24
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞