Objective-C 中的#import 和#include 之间有什么区别?在某些时候你应该使用其中一个而不是另一个?一个被弃用了吗?
我正在阅读以下教程:http : //www.otierney.net/objective-c.html#preamble 及其关于#import 和#include 的段落似乎自相矛盾,或者至少不清楚。
Objective-C 中的#import 和#include 之间有什么区别?在某些时候你应该使用其中一个而不是另一个?一个被弃用了吗?
我正在阅读以下教程:http : //www.otierney.net/objective-c.html#preamble 及其关于#import 和#include 的段落似乎自相矛盾,或者至少不清楚。
关于预处理器似乎有很多困惑。
当编译器看到#include
它用包含文件的内容替换该行时,编译器会做什么,没有提出任何问题。
因此,如果您有一个a.h
包含以下内容的文件:
typedef int my_number;
以及b.c
包含以下内容的文件:
#include "a.h"
#include "a.h"
该文件b.c
将在编译前由预处理器翻译成
typedef int my_number;
typedef int my_number;
这将导致编译器错误,因为该类型my_number
被定义了两次。即使定义相同,C 语言也不允许这样做。
由于标头经常在多个地方使用,因此在 C 中通常使用包含守卫。这看起来像这样:
#ifndef _a_h_included_
#define _a_h_included_
typedef int my_number;
#endif
该文件b.c
在预处理后仍然会包含两次标题的全部内容。但是第二个实例将被忽略,因为宏_a_h_included_
已经定义了。
这非常有效,但有两个缺点。首先必须编写包含保护,并且每个标头中的宏名称必须不同。其次,编译器仍然需要查找头文件并尽可能频繁地读取它。
Objective-C 有#import
预处理器指令(它也可以用于带有一些编译器和选项的 C 和 C++ 代码)。这与 几乎相同#include
,但它还在内部记录了已包含的文件。该#import
行仅在第一次遇到时被命名文件的内容替换。之后每次都会被忽略。
#import 指令作为#include 的改进版本添加到Objective-C。然而,它是否得到改进仍然是一个争论的问题。#import 确保文件只包含一次,这样您就不会遇到递归包含的问题。然而,无论如何,大多数体面的头文件都会保护自己免受这种情况的影响,所以它并没有太大的好处。
基本上,由您决定要使用哪个。我倾向于为 Objective-C 的东西(如类定义等)和 #include 我需要的标准 C 东西 #import 标头。例如,我的一个源文件可能如下所示:
#import <Foundation/Foundation.h>
#include <asl.h>
#include <mach/mach.h>
我同意杰森。
我被抓到这样做:
#import <sys/time.h> // to use gettimeofday() function
#import <time.h> // to use time() function
对于 GNU gcc,它一直抱怨没有定义 time() 函数。
然后我将#import 更改为#include,一切正常。
原因:
你#import <sys/time.h>:
<sys/time.h>使用#defines 只包含<time.h>的一部分
你#import <time.h>:
不行。尽管只有 <time.h> 的一部分已经包含在内,但
就#import 而言,该文件现在已经完全包含在内。
底线:
C/C++ 头文件通常包含其他包含文件的一部分。
所以对于 C/C++ 头文件,使用#include。
对于 objc/objc++ 头文件,使用#import。
#include
就像 C 一样工作#include
。
#import
跟踪已包含哪些标头,如果在编译单元中多次导入标头,则将其忽略。这使得没有必要使用标头保护。
底线只是#import
在 Objective-C 中使用,如果您的标头最终多次导入某些内容,请不要担心。
我知道这个线程很旧......但在“现代”......通过clang的@import
模块有一个非常优越的“包含策略” - 这是经常被忽视的......
模块通过用更健壮、更高效的语义模型替换文本预处理器包含模型来改进对软件库 API 的访问。从用户的角度来看,代码看起来只是略有不同,因为它使用了导入声明而不是#include 预处理器指令:
@import Darwin; // Like including all of /usr/include. @see /usr/include/module.map
或者
@import Foundation; // Like #import <Foundation/Foundation.h>
@import ObjectiveC; // Like #import <objc/runtime.h>
但是,此模块导入的行为与相应的#include 完全不同:当编译器看到上面的模块导入时,它会加载模块的二进制表示并使其 API 直接可供应用程序使用。导入声明之前的预处理器定义对提供的 API 没有影响……因为模块本身被编译为单独的独立模块。此外,在导入模块时,将自动提供使用该模块所需的任何链接器标志。这种语义导入模型解决了预处理器包含模型的许多问题。
要启用模块,请在编译时传递命令行标志,-fmodules
也就是CLANG_ENABLE_MODULES
- 。Xcode
如上所述.. 这种策略避免了 ANY 和 ALL LDFLAGS
。如在,您可以删除任何“OTHER_LDFLAGS”设置,以及任何“链接”阶段..
我发现编译/启动时间“感觉”更快(或者可能,“链接”时延迟更少?)..而且,提供了一个很好的机会来清除现在无关的 Project-Prefix.pch 文件,并且相应的构建设置、GCC_INCREASE_PRECOMPILED_HEADER_SHARING
、GCC_PRECOMPILE_PREFIX_HEADER
和GCC_PREFIX_HEADER
等。
此外,虽然没有很好的文档......您可以module.map
为自己的框架创建 s 并以同样方便的方式包含它们。 您可以查看我的 ObjC-Clang-Modules github repo,了解如何实现这些奇迹的一些示例。
如果你熟悉 C++ 和宏,那么
#import "Class.h"
类似于
{
#pragma once
#include "class.h"
}
这意味着您的类只会在您的应用程序运行时加载一次。
在可能的情况下,我的一个.h
文件中有一个导致问题的全局变量,我通过extern
在它前面添加来解决它。
#include
它曾经从另一个文件中获取“东西”到#include
使用的文件中。例如:
在文件中:main.cpp
#include "otherfile.h"
// some stuff here using otherfile.h objects,
// functions or classes declared inside
在每个头文件 (*.h) 的顶部使用头保护,以防止多次包含同一文件(如果发生这种情况,您将得到编译错误)。
在文件中:otherfile.h
#ifndef OTHERFILE
#define OTHERFILE
// declare functions, classes or objects here
#endif
即使您#include
在代码中放入“otherfile.h”n 次,它里面的 this 也不会被重新声明。
如果你在 .h 文件中 #include 一个文件两次,编译器就会出错。但是如果你多次#import 一个文件,编译器会忽略它。
#include 与 #import
历史:
#include => #import => Precompiled Headers .pch => @import Module(ObjC); => import Module(Swift)
[预编译头文件 .pch]
[@import Module(ObjC);]
[import Module(Swift)]
#include + guard == #import
#include guard
Wiki - 宏守卫、标头守卫或文件守卫可防止双重包含标头,preprocessor
这会减慢构建时间
#进口劣势
.h
适用于文件范围,这就是我们构建时间缓慢的原因,因为编译器必须解析和编译与项目范围中导入的文件一样多的次数