В чем разница между #import и #include в Objective-C?

385

Каковы различия между #import и #include в Objective-C, и есть ли моменты, когда вы должны использовать один над другим? Один устарел?

Я читал следующий учебник: http://www.otierney.net/objective-c.html#preamble и его параграф о #import и #include, кажется, противоречит сам себе или, по крайней мере, неясен.

Райан Гилл
источник

Ответы:

340

Директива #import была добавлена ​​в Objective-C как улучшенная версия #include. Однако все же вопрос о том, улучшился он или нет, все еще остается предметом дискуссий #import гарантирует, что файл будет включен только один раз, чтобы у вас никогда не возникало проблем с рекурсивными включениями. Тем не менее, большинство приличных заголовочных файлов защищают себя от этого в любом случае, так что это не так уж много пользы.

По сути, вам решать, что вы хотите использовать. Я стремлюсь # импортировать заголовки для объектов Objective-C (таких как определения классов и тому подобное) и #include стандартные вещи C, которые мне нужны. Например, один из моих исходных файлов может выглядеть так:

#import <Foundation/Foundation.h>

#include <asl.h>
#include <mach/mach.h>
Джейсон Коко
источник
65
Даже если в заголовочных файлах содержатся защитные элементы include, при компиляции все еще наблюдается снижение производительности, если вы используете #include - компилятор должен открыть каждый заголовочный файл, чтобы заметить защитные элементы include.
Мэтт Диллард
4
защита заголовка - это директива препроцессора, обеспечивающая включение заголовка только один раз в исходный файл.
Джейсон Коко
8
Я думаю, что #import на самом деле является дополнением GCC, а не Objective-C. Вы можете использовать его на языках, отличных от ObjC, если вы компилируете с GCC (или Clang)
Дэйв Делонг,
34
@dave - #import - это дополнение Objective-C к препроцессору. GCC поддерживает его только в исходных файлах C и C ++, хотя они официально предлагают не использовать его в C или C ++ в пользу переносимых традиционных средств защиты заголовков. Однако все препроцессоры Objective-C должны включать #import.
Джейсон Коко
13
Защита заголовка - это то, где вы добавляете к вершине: #ifndef myheader #define myheader ... сопровождается кодом заголовка ...#endif
Тим
359

Кажется, есть много путаницы в отношении препроцессора.

Что делает компилятор, когда он видит, #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 обычно используются защитные элементы. Это выглядит так:

 #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Линия заменяется только содержимое указанного файла в первый раз она встречается. Каждый раз после этого это просто игнорируется.

Sven
источник
5
Это лучший ответ, чем принятый. @Guill, вы должны изменить принятый ответ.
Нгуен Минь Бинь
6
После изменения 4 #includeс на #imports в заголовочном файле шаблона с 7000 строк заметно улучшается производительность компиляции и отзывчивость XCode intellisense. (Я не думаю, что я себе это представляю)
бобобобо
63

Я согласен с Джейсоном.

Я был пойман, делая это:

#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> включаете только часть <time.h>, используя #defines

Вы #import <time.h>:
    нет. Даже если была включена только часть <time.h>, что
    касается #import, этот файл уже полностью включен.

Нижняя граница:

Заголовки C / C ++ традиционно включают в себя части других включаемых файлов.
Так что для заголовков C / C ++ используйте #include.
Для заголовков objc / objc ++ используйте #import.

user512705
источник
2
Кажется, что у Clang нет этой не определенной проблемы.
упс
23

#includeработает так же , как C #include.

#importотслеживает, какие заголовки уже включены, и игнорируется, если заголовок импортирован более одного раза в блок компиляции. Это делает ненужным использование охранников заголовка.

Суть в том, что вы просто используете #importObjective-C и не беспокойтесь, если ваши заголовки импортируют что-то более одного раза.

Ферруччо
источник
2
притворившись на минуту, что я не знаком с C #include (в основном потому, что я не знаком), в чем главное отличие #include от #import? Кроме того, вы можете сказать мне, что такое защита заголовка?
Райан Гилл
@ Райан: Посмотрите на ответ Свена.
Адриан Петреску
13

Я знаю, что эта ветка старая ... но в "современную эпоху" ... гораздо лучше "включить стратегию" через модули 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 ... потому что сам модуль был скомпилирован как отдельный, автономный модуль. Кроме того, любые флаги компоновщика, необходимые для использования модуля, будут автоматически предоставлены при импорте модуля. Эта семантическая модель импорта решает многие проблемы модели включения препроцессора.

Чтобы включить модули, передайте флаг командной строки -fmodulesaka CLANG_ENABLE_MODULESin Xcode- во время компиляции. Как упомянуто выше .. эта стратегия устраняет ЛЮБОЕ и ВСЕ LDFLAGS. Например, вы можете УДАЛИТЬ любые настройки «OTHER_LDFLAGS», а также любые фазы «Связывания».

введите описание изображения здесь

Я нахожу, что время компиляции / запуска «чувствует» намного быстрее (или, может быть, при «связывании» меньше задержки), а также предоставляет отличную возможность очистить теперь посторонний файл Project-Prefix.pch, и соответствующие параметры сборки, GCC_INCREASE_PRECOMPILED_HEADER_SHARING, GCC_PRECOMPILE_PREFIX_HEADER, и GCC_PREFIX_HEADERт.д.

Кроме того, хотя они недостаточно хорошо документированы ... Вы можете создавать module.maps для своих собственных фреймворков и включать их таким же удобным способом. Вы можете взглянуть на мой репозиторий ObjC-Clang-Modules github для некоторых примеров того, как реализовать такие чудеса.

Алекс Грей
источник
4

Если вы знакомы с C ++ и макросами, тогда

#import "Class.h" 

похоже на

{
#pragma once

#include "class.h"
}

Это означает, что ваш класс будет загружен только один раз при запуске приложения.

Evol Gate
источник
Это поддерживаемое использование #pragma один раз? Я всегда думал , прагму необходимое , чтобы быть внутри в ВКЛЮЧАЕТ ред файла на работу.
Uliwitness
@uliwitness Вы правы. #pragma onceпомещается во включаемый файл, а не в файл, который выполняет включение. -1 за это.
herzbube
1

В некоторых случаях у меня была глобальная переменная в одном из моих .hфайлов, которая вызывала проблему, и я решил ее, добавив externперед ней.

neowinston
источник
0

Если вы #include файл два раза в .h файлы, чем компилятор выдаст ошибку. Но если вы импортируете файл более одного раза, компилятор проигнорирует его.

Husmukh
источник
8
#includeодин и тот же файл дважды не приводит к ошибке.
Кеннитм
1
В дополнение к комментарию @ KennyTM, # включение одного и того же файла дважды в один и тот же заголовок не приводит к ошибке компиляции, если имеются обычные заголовочные заголовки (#ifndef FILE_NAME_H #define FILE_NAME_H #end). Это ожидаемая практика. Использование #import защиты заголовка не требуется.
jbat100
@ jbat100: #includeэто просто механизм копирования и вставки. Преднамеренное использование #includeболее одного раза без включения охранников, например, «X macro».
Kennytm
Включение файла дважды может привести к ошибкам в зависимости от того, что вы включаете. Я видел C-код, который использовался #includeдля реализации своего рода шаблонов. Они сделали a #define, включили заголовок, #undefd и переделали #define, включили тот же заголовок во второй раз. Это привело к тому, что код был параметризован, действителен и включен дважды, так как значение определения было другим. Таким образом, у использования есть свои преимущества #include, но если вы используете современный язык, такой как C ++ или ObjC, вам это обычно не нужно.
Uliwitness
0

#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 раз, он не будет переопределен.

Селсо Дантас
источник
0
#include + guard == #import

#include guardWiki - защита от макросов, защита заголовков или защита файлов предотвращает двойное включение заголовкаpreprocessor что может замедлить время сборки

Следующий шаг

.pch[About] =>@import [About]

[# импорт в .hили .m]

yoAlex5
источник