Яка різниця між #import та #include в Objective-C?


384

Які відмінності між #import та #include в Objective-C і чи є випадки, коли вам слід використовувати один над іншим? Чи один застарілий?

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

Відповіді:


341

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

В основному, ви вирішуєте, який ви хочете використовувати. Я схильний #import заголовки для предметів Objective-C (наприклад, визначення класів і подібних) і #include стандартні C речі, які мені потрібні. Наприклад, один із моїх вихідних файлів може виглядати так:

#import <Foundation/Foundation.h>

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

63
Навіть якщо файли заголовків містять охоронні пристрої, під час компіляції все-таки буде досягнуто продуктивності, якщо ви використовуєте #include - компілятор повинен відкривати кожен файл заголовка, щоб помітити включені охоронці.
Метт Діллард

4
Захист заголовка - це директива препроцесора, яка забезпечує заголовка включення лише один раз у вихідний файл.
Джейсон Коко

8
Я думаю, що #import насправді є доповненням GCC, а не Objective-C. Ви можете користуватися нею мовами ObjC, поки ви компілюєте з GCC (або Clang)
Дейв ДеЛонг

33
@dave - #import - це доповнення Objective-C до препроцесора. GCC просто підтримує його у вихідних файлах C та C ++, хоча офіційно пропонують не використовувати його на C або C ++ на користь портативних, традиційних захисних заголовків. Однак усі попередники Objective-C повинні включати #import.
Джейсон Коко

13
Заголовок заголовка - це місце, де ви додасте до початку: #ifndef myheader #define myheader ... а потім код заголовка ...#endif
Тим,

358

Здається, існує велика плутанина щодо препроцесора.

Що робить компілятор, коли бачить, #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Лінія замінюється тільки вміст зазначеного файлу в перший раз вона зустрічається. Кожен раз після цього його просто ігнорують.


4
Це краща відповідь, ніж прийнята. @Guill, слід змінити прийняту відповідь.
Нгуен Мінь Бінь

5
Після зміни 4 #includes на #imports у файлі заголовка рядка шаблону 7000 рядків спостерігається помітне поліпшення продуктивності компіляції та інтелектуальної чутливості XCode. (Я не думаю, що я це собі уявляю)
bobobobo

62

Я погоджуюся з Джейсоном.

Мене зловили, роблячи це:

#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.


1
Здається, у Кланг немає цієї не визначеної проблеми.
ooops

23

#includeпрацює так само , як C #include.

#importвідслідковує, які заголовки вже включені, і ігнорується, якщо заголовок імпортується більше одного разу в одиницю компіляції. Це робить зайвим використовувати захисні заголовки.

Підсумковий рядок - це просто використання #importв Objective-C, і не хвилюйтеся, якщо ваші заголовки закінчуються імпортом чогось більше одного разу.


2
на хвилину роблячи вигляд, що я не знайомий із C #include (здебільшого тому, що мене немає), в чому головна відмінність #include від #import? Також ви можете мені сказати, що таке захисний заголовок?
Райан Гілл

@Ryan: Подивіться на відповідь Свена.
Адріан Петреску

13

Я знаю, що цей потік старий ... але в "сучасні часи" .. існує набагато перевершене "включення стратегії" через модулі Кланг@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 repo, щоб ознайомитись із деякими прикладами того, як здійснити такі чудеса.


4

Якщо ви знайомі з C ++ та макросами, то

#import "Class.h" 

подібний до

{
#pragma once

#include "class.h"
}

а це означає, що ваш клас буде завантажений лише один раз, коли програма працює.


Чи підтримується це #pragma один раз? Я завжди вважав, що прагма повинна знаходитись у включеному файлі Ed, щоб працювати.
uliwitness

@uliwitness Ви праві. #pragma onceрозміщується у включеному файлі, а не у файлі, який виконує включення. -1 за це.
herzbube

1

У моєму випадку у мене був глобальний змінний в одному з моїх .hфайлів, який викликав проблему, і я вирішив її, додавши externперед цим.


0

ЯКЩО ви #include файл два рази у .h файлах, ніж компілятор дасть помилку. Але якщо ви #import файл більше, ніж один раз компілятор буде ігнорувати його.


8
#includeодин і той же файл двічі не призводить до помилки.
kennytm

1
Щоб доповнити коментар @ KennyTM, # включення одного і того ж файлу двічі в один і той же заголовок не призводить до помилки компіляції, якщо звичайні заголовки (#ifndef FILE_NAME_H #define FILE_NAME_H #end) є. Це очікувана практика. Використання #import захисних заголовків не потрібно.
jbat100

@ jbat100: #includeце просто механізм копіювання та вставки. Існує свідоме використання #includeбільше одного разу без включення охоронців, наприклад, "макрос X".
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 "код іншихfile.h" n разів у свій код, це всередині нього не буде переглянуто.


0
#include + guard == #import

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

Наступний крок -

.pch[Про] =>@import [Про]

[#імпортувати в .hабо .m]

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.