ChatGPT解决这个技术问题 Extra ChatGPT

Objective-C 中的#import 和#include 有什么区别?

Objective-C 中的#import 和#include 之间有什么区别?在某些时候你应该使用其中一个而不是另一个?一个被弃用了吗?

我正在阅读以下教程:http://www.otierney.net/objective-c.html#preamble 及其关于#import 和#include 的段落似乎自相矛盾,或者至少不清楚。


S
Sven

关于预处理器似乎有很多困惑。

编译器在看到 #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 行仅在第一次遇到时被命名文件的内容替换。之后每次都会被忽略。


这是比接受的更好的答案。 @Guill,您应该更改接受的答案。
在 7000 行模板头文件上将 4 #include 更改为 #import 后,编译和 XCode 智能感知响应能力有明显的改进。 (我不认为我在想象)
R
Reto Koradi

#import 指令作为#include 的改进版本添加到Objective-C。然而,它是否得到改进仍然是一个争论的问题。 #import 确保文件只包含一次,这样您就不会遇到递归包含的问题。然而,无论如何,大多数体面的头文件都会保护自己免受这种情况的影响,所以它并没有太大的好处。

基本上,由您决定要使用哪个。我倾向于为 Objective-C 的东西(如类定义等)和 #include 我需要的标准 C 东西 #import 标头。例如,我的一个源文件可能如下所示:

#import <Foundation/Foundation.h>

#include <asl.h>
#include <mach/mach.h>

即使头文件包含包含保护,如果您使用#include,编译期间仍然会影响性能——编译器必须打开每个头文件才能注意到包含保护。
我认为#import 实际上是 GCC 的补充,而不是 Objective-C 的补充。只要您使用 GCC(或 Clang)编译,就可以在非 ObjC 语言中使用它
@dave - #import 是 Objective-C 对预处理器的补充。 GCC 也仅在 C 和 C++ 源文件中支持它,尽管他们官方建议不要在 C 或 C++ 中使用它,而是支持可移植的传统标头保护。但是,所有 Objective-C 预处理器都必须包含#import。
标头保护是您添加到顶部的位置:#ifndef myheader #define myheader ... 后跟标头代码... #endif
这不是标准与非标准;这是语言与语言,一个意图与另一个意图。如果您使用的是 Objective-C 并且打算包含 Objective-C 标头,请使用#import。如果您使用 C、C++ 或使用 Objective-C 并且只想将一个文件内联到另一个文件中,请使用 #include。
b
bdesham

我同意杰森。

我被抓到这样做:

#import <sys/time.h>  // to use gettimeofday() function
#import <time.h>      // to use time() function

对于 GNU gcc,它一直抱怨没有定义 time() 函数。

然后我将#import 更改为#include,一切正常。

原因:

你#import 使用#defines 只包含 的一部分

你#import :不行。尽管只有 的一部分已经包含在内,但就#import 而言,该文件现在已经完全包含在内。

底线:

C/C++ 头文件通常包含其他包含文件的一部分。所以对于 C/C++ 头文件,使用#include。对于 objc/objc++ 头文件,使用#import。


似乎clang没有这个未定义的问题。
这是一个很好的例子,说明了为什么以非侵入性的方式将现代功能 (#import) 引入现有系统具有挑战性。是否应该更新系统库以更好地处理#import ...可能不会。如果是这样,这将以牺牲许多有意或无意地依赖现有行为的现有项目为代价。语言开发是否应该停止引入新的、有用的、前瞻性的功能……不。因此,它从来没有像对已接受答案的一些评论所暗示的那样简单。
这是一个糟糕的书面包含文件的明显问题。 gcc 承认了这一点,并重新构建了他们的头文件。头文件不应该依赖于任何包含顺序。
F
Ferruccio

#include 的工作方式与 C #include 类似。

#import 跟踪已包含哪些标头,如果在编译单元中多次导入标头,则将其忽略。这使得没有必要使用标头保护。

底线只是在 Objective-C 中使用 #import 并且不要担心你的标题是否会多次导入某些内容。


假装我不熟悉 C #include(主要是因为我不熟悉),#include 和 #import 之间的主要区别是什么?另外,你能告诉我什么是头球后卫吗?
@Ryan:看看斯文的回答。
A
Alex Gray

我知道这个线程很旧......但在“现代”......通过clang's @import modules有一个更优越的“包含策略” - 这是经常被忽视的......

模块通过用更健壮、更高效的语义模型替换文本预处理器包含模型来改进对软件库 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 没有影响……因为模块本身被编译为单独的独立模块。此外,在导入模块时,将自动提供使用该模块所需的任何链接器标志。这种语义导入模型解决了预处理器包含模型的许多问题。

要启用模块,请在编译时在 Xcode 中传递命令行标志 -fmodules aka CLANG_ENABLE_MODULES。如上所述.. 这种策略避免了 ANY 和 ALL LDFLAGS。如在,您可以删除任何“OTHER_LDFLAGS”设置,以及任何“链接”阶段..

https://i.stack.imgur.com/yUi2T.png

我发现编译/启动时间“感觉”更快(或者可能,“链接”时延迟更少?)..而且,提供了一个很好的机会来清除现在无关的 Project-Prefix.pch 文件,并且相应的构建设置,GCC_INCREASE_PRECOMPILED_HEADER_SHARINGGCC_PRECOMPILE_PREFIX_HEADERGCC_PREFIX_HEADER 等。

此外,虽然没有充分记录……您可以为自己的框架创建 module.map,并以同样方便的方式包含它们。 You can take a look at my ObjC-Clang-Modules github repo for some examples of how to implement such miracles.


L
LittleBobbyTables - Au Revoir

如果你熟悉 C++ 和宏,那么

#import "Class.h" 

类似于

{
#pragma once

#include "class.h"
}

这意味着您的类只会在您的应用程序运行时加载一次。


这是一次受支持的#pragma 用法吗?我一直认为编译指示需要在包含的文件中才能工作。
@uliwitness你是对的。 #pragma once 放置在包含的文件中,而不是执行包含的文件中。 -1 为此。
n
neowinston

在可能的情况下,我的一个 .h 文件中有一个导致问题的全局变量,我通过在它前面添加 extern 来解决它。


y
yoAlex5

#include 与 #import

历史:

#include => #import => Precompiled Headers .pch => @import Module(ObjC); => import Module(Swift)

[Precompiled Headers .pch]
[@import Module(ObjC);]
[import Module(Swift)]

#include + guard == #import

#include guardWiki - 宏保护、标头保护或文件保护可防止通过 preprocessor 双重包含标头,这会减慢构建时间

#进口劣势

适用于文件范围,这就是我们构建时间缓慢的原因,因为编译器必须解析和编译与项目范围中导入的 .h 文件一样多的次数

[#import in .h or .m]


H
Husmukh

如果你在 .h 文件中 #include 一个文件两次,编译器就会出错。但是如果你多次#import 一个文件,编译器会忽略它。


#include 同一文件两次不会导致错误。
为了补充@KennyTM 的评论,如果存在通常的头文件(#ifndef FILE_NAME_H #define FILE_NAME_H #end),则在同一头文件中对同一文件进行两次#include-ing 不会导致编译错误。这是预期的做法。不需要使用 #import 标头保护。
@jbat100:#include 只是一种复制粘贴机制。在没有包含保护的情况下多次故意使用 #include,例如“X 宏”。
包含两次文件可能会导致错误,具体取决于您包含的内容。我见过使用 #include 实现一种模板的 C 代码。他们做了一个 #define,包含了一个标题,#undefd 并重新做了 #define,第二次包含了相同的标题。这导致代码被参数化、有效并包含两次,因为定义的值不同。所以使用 #include 有一些优势,但如果您使用的是现代语言,如 C++ 或 ObjC,则通常不需要它。
L
LPL

#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 也不会被重新声明。