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 等等在编译时的作用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 模块化工作中,会指定库与库之间的依赖关系,根据依赖关系分层,但随着开发进行,依赖关系又慢慢被破坏。如何让后续的开发...
    donghuan1阅读 3,825评论 0 3
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,590评论 18 139
  • PLEASE READ THE FOLLOWING APPLE DEVELOPER PROGRAM LICENSE...
    念念不忘的阅读 13,430评论 5 6
  • 前几日围观了一场“以小欺大”的舌战,两个大男人争得面红耳赤,彼此都使出了毕生的洪荒之力,少年此时看来略站上风。其实...
    七琰阅读 363评论 2 4
  • 今天早上妈妈完成趣配音6-3,宝哥今天起床后发现有一个学校作业漏做了,赶快补做后剩给英语的时间又不多了。跟...
    小舟2017阅读 253评论 0 0