Was ist der Unterschied zwischen #import und #include in Objective-C?

385

Was sind die Unterschiede zwischen #import und #include in Objective-C und gibt es Zeiten, in denen Sie einen über den anderen verwenden sollten? Ist man veraltet?

Ich habe das folgende Tutorial gelesen: http://www.otierney.net/objective-c.html#preamble und sein Absatz über #import und #include scheint sich selbst zu widersprechen oder ist zumindest unklar.

Ryan Guill
quelle

Antworten:

340

Die Direktive #import wurde zu Objective-C als verbesserte Version von #include hinzugefügt. Ob es jedoch verbessert wird oder nicht, ist immer noch umstritten. #import stellt sicher, dass eine Datei immer nur einmal enthalten ist, sodass Sie nie ein Problem mit rekursiven Includes haben. Die meisten anständigen Header-Dateien schützen sich jedoch trotzdem davor, so dass dies nicht wirklich ein großer Vorteil ist.

Grundsätzlich liegt es an Ihnen, zu entscheiden, welche Sie verwenden möchten. Ich neige dazu, # Header für Objective-C-Dinge (wie Klassendefinitionen und dergleichen) zu importieren und # Standard-C-Sachen einzuschließen, die ich brauche. Zum Beispiel könnte eine meiner Quelldateien so aussehen:

#import <Foundation/Foundation.h>

#include <asl.h>
#include <mach/mach.h>
Jason Coco
quelle
64
Selbst wenn Header-Dateien Include-Guards enthalten, tritt beim Kompilieren immer noch ein Leistungseinbruch auf, wenn Sie #include verwenden. Der Compiler muss jede Header-Datei öffnen, um die Include-Guards zu erkennen.
Matt Dillard
4
Ein Header Guard ist eine Präprozessor-Direktive, die sicherstellt, dass ein Header nur einmal in einer Quelldatei enthalten ist.
Jason Coco
8
Ich denke, #import ist eigentlich eine Ergänzung von GCC, nicht von Objective-C. Sie können es in Nicht-ObjC-Sprachen verwenden, solange Sie mit GCC (oder Clang) kompilieren
Dave DeLong
34
@dave - #import ist eine Objective-C-Ergänzung zum Präprozessor. GCC unterstützt es nur in C- und C ++ - Quelldateien, obwohl offiziell empfohlen wird, es nicht in C oder C ++ zu verwenden, um tragbare, traditionelle Header-Schutzvorrichtungen zu verwenden. Alle Objective-C-Präprozessoren müssen jedoch #import enthalten.
Jason Coco
13
Ein Header Guard ist, wo Sie oben hinzufügen: #ifndef myheader #define myheader ... gefolgt von Header-Code ...#endif
Tim
359

Es scheint viel Verwirrung hinsichtlich des Präprozessors zu geben.

Was der Compiler tut, wenn er sieht, #includedass er diese Zeile durch den Inhalt der enthaltenen Dateien ersetzt, ohne dass Fragen gestellt werden.

Wenn Sie also eine Datei a.hmit diesem Inhalt haben:

typedef int my_number;

und eine Datei b.cmit diesem Inhalt:

#include "a.h"
#include "a.h"

Die Datei b.cwird vom Präprozessor vor dem Kompilieren nach übersetzt

typedef int my_number;
typedef int my_number;

Dies führt zu einem Compilerfehler, da der Typ my_numberzweimal definiert wird. Obwohl die Definition dieselbe ist, ist dies in der C-Sprache nicht zulässig.

Da ein Header häufig an mehr als einer Stelle verwendet wird, werden in C normalerweise Include-Schutzvorrichtungen verwendet. Dies sieht folgendermaßen aus:

 #ifndef _a_h_included_
 #define _a_h_included_

 typedef int my_number;

 #endif

Die Datei b.cwürde nach der Vorverarbeitung noch zweimal den gesamten Inhalt des Headers enthalten. Die zweite Instanz würde jedoch ignoriert, da das Makro _a_h_included_bereits definiert worden wäre.

Das funktioniert sehr gut, hat aber zwei Nachteile. Zunächst müssen die Include-Guards geschrieben werden, und der Makroname muss in jedem Header unterschiedlich sein. Und zweitens muss der Compiler noch nach der Header-Datei suchen und sie so oft lesen, wie sie enthalten ist.

Objective-C verfügt über die #importPräprozessoranweisung (sie kann mit einigen Compilern und Optionen auch für C- und C ++ - Code verwendet werden). Dies macht fast das Gleiche wie #include, merkt aber auch intern an, welche Datei bereits enthalten ist. Die #importZeile wird erst beim ersten Auftreten durch den Inhalt der benannten Datei ersetzt. Jedes Mal danach wird es einfach ignoriert.

Sven
quelle
5
Dies ist die bessere Antwort als akzeptiert. @ Guill, du solltest die akzeptierte Antwort ändern.
Nguyen Minh Binh
6
Nach dem Ändern von 4 #includes in #imports in einer 7000-Zeilenvorlagen-Headerdatei wird eine deutliche Leistungsverbesserung bei der Kompilierung und der Reaktionsfähigkeit von XCode Intellisense festgestellt. (Ich glaube nicht, dass ich es mir vorstelle)
Bobobobo
63

Ich stimme Jason zu.

Ich wurde dabei erwischt:

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

Für GNU gcc wurde immer wieder beanstandet, dass die Funktion time () nicht definiert wurde.

Also habe ich #import in #include geändert und alles ging in Ordnung.

Grund:

Sie #importieren <sys / time.h>:
    <sys / time.h> enthält nur einen Teil von <time.h>, indem Sie #defines verwenden

Sie #importieren <time.h>:
    Nein. Obwohl nur ein Teil von <time.h> bereits enthalten war,
    ist diese Datei für #import bereits vollständig enthalten.

Endeffekt:

C / C ++ - Header enthalten traditionell Teile anderer Include-Dateien.
Verwenden Sie für C / C ++ - Header #include.
Verwenden Sie für objc / objc ++ - Header #import.

user512705
quelle
2
Es scheint, dass Clang dieses nicht definierte Problem nicht hat.
ooops
23

#includefunktioniert genauso wie das C #include.

#importVerfolgt, welche Header bereits enthalten sind, und wird ignoriert, wenn ein Header mehrmals in eine Kompilierungseinheit importiert wird. Dies macht es unnötig, Header Guards zu verwenden.

Das Endergebnis wird nur #importin Objective-C verwendet und Sie müssen sich keine Sorgen machen, wenn Ihre Header mehr als einmal importieren.

Ferruccio
quelle
2
Was ist der Hauptunterschied zwischen #include und #import? Können Sie mir auch sagen, was ein Header Guard ist?
Ryan Guill
@ Ryan: Schau dir Svens Antwort an.
Adrian Petrescu
13

Ich weiß, dass dieser Thread alt ist ... aber in "modernen Zeiten" ... gibt es eine weit überlegene "Include-Strategie" über Clangs @importModule - das wird oft übersehen ...

Module verbessern den Zugriff auf die API von Softwarebibliotheken, indem sie das Einschlussmodell für Textpräprozessoren durch ein robusteres, effizienteres semantisches Modell ersetzen. Aus Anwendersicht sieht der Code nur geringfügig anders aus, da anstelle einer # include-Präprozessor-Direktive eine Importdeklaration verwendet wird:

@import Darwin; // Like including all of /usr/include. @see /usr/include/module.map

oder

@import Foundation;  //  Like #import <Foundation/Foundation.h>
@import ObjectiveC;  //  Like #import <objc/runtime.h>

Dieser Modulimport verhält sich jedoch ganz anders als der entsprechende #include: Wenn der Compiler den obigen Modulimport sieht, lädt er eine binäre Darstellung des Moduls und stellt seine API der Anwendung direkt zur Verfügung. Präprozessordefinitionen, die der Importdeklaration vorausgehen, haben keine Auswirkungen auf die bereitgestellte API ... da das Modul selbst als separates, eigenständiges Modul kompiliert wurde. Darüber hinaus werden alle Linker-Flags, die zur Verwendung des Moduls erforderlich sind, automatisch bereitgestellt, wenn das Modul importiert wird. Dieses semantische Importmodell behandelt viele der Probleme des Präprozessor-Einschlussmodells.

Übergeben Sie zum Aktivieren von Modulen das Befehlszeilenflag -fmodulesaka CLANG_ENABLE_MODULESin Xcode- zur Kompilierungszeit. Wie oben erwähnt, vermeidet diese Strategie JEDES und ALLES LDFLAGS. Wie in können Sie alle "OTHER_LDFLAGS" -Einstellungen sowie alle "Linking" -Phasen ENTFERNEN.

Geben Sie hier die Bildbeschreibung ein

Ich finde Kompilierungs- / Startzeiten, um mich viel schneller zu "fühlen" (oder möglicherweise gibt es beim "Verknüpfen" nur eine geringere Verzögerung?). Außerdem bietet es eine großartige Möglichkeit, die jetzt überflüssige Project-Prefix.pch-Datei zu löschen, und entsprechende Build - Einstellungen GCC_INCREASE_PRECOMPILED_HEADER_SHARING, GCC_PRECOMPILE_PREFIX_HEADERund GCC_PREFIX_HEADERetc.

Auch wenn dies nicht gut dokumentiert ist… Sie können module.maps für Ihre eigenen Frameworks erstellen und diese auf dieselbe bequeme Weise einbinden. In meinem Github-Repo für ObjC-Clang-Module finden Sie einige Beispiele für die Umsetzung solcher Wunder.

Alex Gray
quelle
4

Wenn Sie mit C ++ und Makros vertraut sind, dann

#import "Class.h" 

ist ähnlich wie

{
#pragma once

#include "class.h"
}

Dies bedeutet, dass Ihre Klasse nur einmal geladen wird, wenn Ihre App ausgeführt wird.

Evol Gate
quelle
Ist dies eine unterstützte Verwendung von #pragma einmal? Ich dachte immer , die Pragma sein musste innerhalb der includ ed - Datei an die Arbeit.
uliwitness
@uliwitness Du bist richtig. #pragma oncewird in die eingeschlossene Datei eingefügt, nicht in die Datei, die das Einschließen ausführt. -1 dafür.
Herzbube
1

In einem Fall hatte ich möglicherweise eine globale Variable in einer meiner .hDateien, die das Problem verursachte, und ich löste sie, indem ich sie externdavor hinzufügte .

Neowinston
quelle
0

Wenn Sie eine Datei zweimal in .h-Dateien einschließen, gibt der Compiler einen Fehler aus. Wenn Sie eine Datei jedoch mehrmals importieren, wird sie vom Compiler ignoriert.

Husmukh
quelle
8
#includeDie gleiche Datei führt zweimal nicht zu einem Fehler.
Kennytm
1
Um den Kommentar von @ KennyTM zu ergänzen, führt # das zweimalige Einfügen derselben Datei in denselben Header nicht zu einem Kompilierungsfehler, wenn die üblichen Header-Gards (#ifndef FILE_NAME_H #define FILE_NAME_H #end) vorhanden sind. Dies ist erwartete Praxis. Mit #import werden die Header Guards nicht benötigt.
jbat100
@ jbat100: #includeist einfach ein Mechanismus zum Kopieren und Einfügen. Es wird absichtlich #includemehr als einmal ohne Include-Schutz verwendet, z. B. das "X-Makro".
Kennytm
Das zweimalige Einfügen einer Datei kann je nach dem, was Sie einschließen, zu Fehlern führen. Ich habe C-Code gesehen, mit #includedem eine Art Vorlagen implementiert wurden. Sie haben a gemacht #define, einen Header #undefeingefügt , d und den #define, ein zweites Mal den gleichen Header eingefügt . Dies führte dazu, dass der Code zweimal parametrisiert, gültig und eingeschlossen wurde, da der Wert der Definition unterschiedlich war. Die Verwendung #includebietet also Vorteile , aber wenn Sie eine moderne Sprache wie C ++ oder ObjC verwenden, benötigen Sie diese im Allgemeinen nicht.
uliwitness
0

#includeFrüher wurden "Dinge" aus einer anderen Datei in die Datei übertragen, in der sie #includeverwendet werden. Beispiel:

in Datei: main.cpp

#include "otherfile.h"

// some stuff here using otherfile.h objects,
// functions or classes declared inside

Der Header Guard wird oben in jeder Header-Datei (* .h) verwendet, um zu verhindern, dass dieselbe Datei mehrmals eingefügt wird (in diesem Fall werden Kompilierungsfehler angezeigt).

in Datei: otherfile.h

#ifndef OTHERFILE
#define OTHERFILE

// declare functions, classes or objects here

#endif

Selbst wenn Sie #include"otherfile.h" n-mal in Ihren Code einfügen, wird dieser darin nicht erneut deklariert.

Celso Dantas
quelle
0
#include + guard == #import

#include guardWiki - Makro-Schutz, Header-Schutz oder Datei-Schutz verhindert, dass ein Header durch einen doppelt eingeschlossen wirdpreprocessor, was die Erstellungszeit verlangsamen kann

Der nächste Schritt ist

.pch[Über] =>@import [Über]

[#import in .hoder .m]

yoAlex5
quelle