@klasse vs #import

687

Nach meinem Verständnis sollte man eine forward-class-Deklaration für den Fall verwenden, dass ClassA einen ClassB-Header enthalten muss und ClassB einen ClassA-Header enthalten muss, um zirkuläre Einschlüsse zu vermeiden. Ich verstehe auch, dass ein #import ein einfaches ifndef ist, so dass ein Include nur einmal passiert.

Meine Frage lautet: Wann benutzt man #import und wann benutzt man @class ? Manchmal, wenn ich eine @class -Deklaration verwende, sehe ich eine allgemeine Compiler-Warnung wie die folgende:

  

warning: receiver 'FooController' is a forward class and corresponding @interface may not exist.

Würde es wirklich lieben, das zu verstehen, statt nur die @class forward-Deklaration zu entfernen und ein #import in zu werfen, um die Warnungen des Compilers zum Schweigen zu bringen.

    
Coocoo4Cocoa 27.11.2008, 00:20
quelle

16 Antworten

728

Wenn Sie diese Warnung sehen:

  

Warnung: Empfänger 'MyCoolClass' ist eine Forward Klasse und die entsprechende @ Schnittstelle darf nicht existieren

Sie müssen #import die Datei angeben, aber Sie können dies in Ihrer Implementierungsdatei (.m) tun und die Deklaration @class in Ihrer Header-Datei verwenden.

@class entfernt (normalerweise) nicht die Notwendigkeit für #import -Dateien, sondern verschiebt die Anforderung einfach dorthin, wo die Informationen nützlich sind.

Beispiel

Wenn Sie @class MyCoolClass sagen, weiß der Compiler, dass er etwas wie folgt sehen kann:

%Vor%

Es muss sich um nichts anderes kümmern als MyCoolClass ist eine gültige Klasse, und es sollte Platz für einen Zeiger auf es (wirklich, nur ein Zeiger) reservieren. Daher reicht @class in Ihrer Kopfzeile zu 90% der Zeit aus.

Wenn Sie jedoch jemals Mitglieder von myObject erstellen oder darauf zugreifen müssen, müssen Sie dem Compiler mitteilen, was diese Methoden sind. An dieser Stelle (vermutlich in Ihrer Implementierungsdatei) müssen Sie #import "MyCoolClass.h" angeben, um dem Compiler zusätzliche Informationen mitzuteilen, die über "Dies ist eine Klasse" hinausgehen.

    
Ben Gottlieb 27.11.2008, 00:33
quelle
175

Drei einfache Regeln:

  • Nur #import die Superklasse und übernommene Protokolle in Headerdateien ( .h files).
  • #import alle Klassen und Protokolle, an die Sie in der Implementierung Nachrichten senden ( .m files).
  • Weiterleitungen für alles andere.

Wenn Sie die Deklaration in den Implementierungsdateien weiterleiten, tun Sie wahrscheinlich etwas falsch.

    
PeyloW 29.08.2009 00:34
quelle
108

Sehen Sie sich die Dokumentation zur Objective-C-Programmiersprache für ADC an

Unter dem Abschnitt zum Definieren einer Klasse | Class Interface beschreibt, warum dies gemacht wird:

  

Die @ class -Direktive minimiert die Menge an Code, die vom Compiler und Linker gesehen wird, und ist daher der einfachste Weg, eine Vorwärtsdeklaration eines Klassennamens zu geben. Da es einfach ist, vermeidet es potentielle Probleme, die beim Importieren von Dateien auftreten können, die noch andere Dateien importieren. Wenn beispielsweise eine Klasse eine statisch typisierte Instanzvariable einer anderen Klasse deklariert und ihre beiden Schnittstellendateien sich gegenseitig importieren, kann keine der beiden Klassen korrekt kompiliert werden.

Ich hoffe, das hilft.

    
Abizern 27.11.2008 11:23
quelle
47

Verwenden Sie bei Bedarf eine Vorwärtsdeklaration in der Headerdatei und #import die Headerdateien für alle Klassen, die Sie in der Implementierung verwenden. Mit anderen Worten, Sie haben immer #import die Dateien, die Sie in Ihrer Implementierung verwenden, und wenn Sie eine Klasse in Ihrer Header-Datei referenzieren müssen, verwenden Sie auch eine Forward-Deklaration.

Die Ausnahme ist, dass Sie #import ein Klassen- oder formales Protokoll verwenden sollten, von dem Sie in Ihrer Header-Datei übernehmen (in diesem Fall müssten Sie es nicht in die Implementierung importieren) ).

    
Marc Charbonneau 27.11.2008 00:33
quelle
24

Die gängige Praxis ist die Verwendung von @class in Header-Dateien (Sie müssen jedoch immer noch die Superklasse importieren) und in Implementationsdateien importieren. Dadurch werden runde Einschlüsse vermieden und es funktioniert einfach.

    
Steph Thirion 27.11.2008 01:04
quelle
24

Ein weiterer Vorteil: Schnelle Kompilierung

Wenn Sie eine Header-Datei einfügen, führt jede Änderung dazu, dass die aktuelle Datei ebenfalls kompiliert wird. Dies ist jedoch nicht der Fall, wenn der Klassenname als @class name enthalten ist. Natürlich müssen Sie den Header in die Quelldatei einfügen

    
vent 09.09.2010 08:02
quelle
18
  

Meine Anfrage ist das. Wann benutzt man #import und wann benutzt man @class?

Einfache Antwort: Sie haben #import oder #include , wenn eine physische Abhängigkeit besteht. Andernfalls verwenden Sie Terminkalender ( @class MONClass , struct MONStruct , @protocol MONProtocol ).

Hier sind einige Beispiele für physische Abhängigkeit:

  • Jeder C- oder C ++ - Wert (ein Zeiger oder eine Referenz ist keine physische Abhängigkeit). Wenn Sie ein CGPoint als Ivar oder Eigenschaft haben, muss der Compiler die Deklaration von CGPoint .
  • sehen
  • Deine Oberklasse.
  • Eine Methode, die Sie verwenden.
  

Manchmal, wenn ich eine @ class-Deklaration verwende, sehe ich eine allgemeine Compiler-Warnung wie die folgende:      "Warnung: Empfänger 'FooController' ist eine Weiterleitungsklasse und die entsprechende @ Schnittstelle darf nicht existieren."

Der Compiler ist in dieser Hinsicht tatsächlich sehr nachsichtig. Es werden Hinweise (wie oben) fallengelassen, aber du kannst deinen Stack leicht zerstören, wenn du sie ignorierst und #import nicht richtig verwendest. Obwohl dies der Fall sein sollte (IMO), erzwingt der Compiler dies nicht. In ARC ist der Compiler strenger, da er für das Referenzzählen verantwortlich ist. Was passiert, ist, dass der Compiler auf einen Standard zurückgreift, wenn er auf eine unbekannte Methode trifft, die Sie aufrufen. Jeder Rückgabewert und Parameter wird als id angenommen. Daher sollten Sie jede Warnung aus Ihren Codebasen löschen, da dies als physische Abhängigkeit betrachtet werden sollte. Dies ist analog zum Aufruf einer C-Funktion, die nicht deklariert ist. Mit C werden Parameter als int angenommen.

Der Grund dafür, dass Sie Forward-Deklarationen bevorzugen, ist, dass Sie Ihre Build-Zeiten durch Faktoren reduzieren können, da nur eine minimale Abhängigkeit besteht. Bei Vorwärtsdeklarationen sieht der Compiler einen Namen und kann das Programm korrekt analysieren und kompilieren, ohne die Klassendeklaration oder alle zugehörigen Abhängigkeiten zu sehen, wenn keine physische Abhängigkeit besteht. Saubere Builds benötigen weniger Zeit. Inkrementelle Builds benötigen weniger Zeit. Sicher, Sie werden am Ende ein wenig mehr Zeit damit verbringen, sicherzustellen, dass alle Header, die Sie benötigen, für jede Übersetzung als Konsequenz sichtbar sind, aber das rechnet sich in kurzen Bauzeiten schnell (vorausgesetzt, Ihr Projekt ist nicht winzig).

Wenn Sie stattdessen #import oder #include verwenden, ersparen Sie dem Compiler mehr Arbeit als nötig. Sie führen auch komplexe Header-Abhängigkeiten ein. Sie können dies mit einem Brute-Force-Algorithmus vergleichen. Wenn Sie #import verwenden, ziehen Sie eine Unmenge an unnötigen Informationen, was viel Speicher, Festplatten-I / O und CPU erfordert, um die Quellen zu analysieren und zu kompilieren.

ObjC ist für eine C-basierte Sprache hinsichtlich der Abhängigkeit ziemlich ideal, weil NSObject -Typen niemals Werte sind - NSObject -Typen sind immer Referenzzähler. So können Sie mit unglaublich schnellen Kompilierzeiten durchkommen, wenn Sie die Abhängigkeiten Ihres Programms entsprechend strukturieren und wenn möglich weiterleiten, da nur sehr wenig physische Abhängigkeit erforderlich ist. Sie können auch Eigenschaften in den Klassenerweiterungen deklarieren, um die Abhängigkeit weiter zu minimieren. Das ist ein großer Bonus für große Systeme - Sie würden den Unterschied kennen, den es macht, wenn Sie jemals eine große C ++ - Codebasis entwickelt haben.

Deshalb empfehle ich, vorwärts zu gehen, wo es möglich ist, und dann zu #import , wo es physische Abhängigkeit gibt. Wenn Sie die Warnung sehen oder eine andere, die körperliche Abhängigkeit impliziert, beheben Sie sie alle. Das Update ist #import in Ihrer Implementierungsdatei.

Beim Erstellen von Bibliotheken werden Sie wahrscheinlich einige Schnittstellen als eine Gruppe klassifizieren. In diesem Fall würden Sie #import der Bibliothek angeben, in der die physische Abhängigkeit eingeführt wird (z. B. #import <AppKit/AppKit.h> ). Dies kann zu Abhängigkeiten führen, aber die Bibliotheksbetreuer können die physischen Abhängigkeiten für Sie oft nach Bedarf handhaben. Wenn sie eine Funktion einführen, können sie die Auswirkungen auf Ihre Builds minimieren.

    
justin 08.02.2012 06:10
quelle
11

Ich sehe eine Menge "Tu es so", aber ich sehe keine Antworten auf "Warum?"

Also: Warum sollten Sie in Ihrer Kopfzeile nur in der Kopfzeile und nur in der Implementierung #importieren? Du verdoppelst deine Arbeit, indem du ständig @class und #import machst. Es sei denn, Sie nutzen die Vererbung. In diesem Fall werden Sie mehrfach für eine einzelne @ Klasse importiert. Dann müssen Sie daran denken, aus mehreren verschiedenen Dateien zu entfernen, wenn Sie plötzlich entscheiden, dass Sie keinen Zugriff mehr auf eine Deklaration benötigen.

Das mehrfache Importieren der gleichen Datei ist aufgrund der Art von #import kein Problem.  Kompilieren der Leistung ist auch kein Problem. Wenn es so wäre, würden wir Cocoa / Cocoa.h oder ähnliches nicht in so ziemlich jede Header-Datei importieren, die wir haben.

    
Bruce Goodwin 29.06.2011 15:51
quelle
7

wenn wir das tun

%Vor%

bedeutet, dass wir die Klasse_A in Klasse_B erben, in Klasse_B können wir auf alle Variablen von Klasse_A zugreifen.

wenn wir das tun

%Vor%

hier sagen wir, dass wir die Klasse_A in unserem Programm verwenden, aber wenn wir die Class_A Variablen in Class_B verwenden wollen, müssen wir Class_A in .m Datei importieren (ein Objekt erstellen und dessen Funktion und Variablen verwenden).

    
Anshuman Mishra 04.01.2012 06:32
quelle
5

für zusätzliche Informationen über Dateiabhängigkeiten & amp; #import & amp; @klasse überprüfe das:

Ссылка Es ist ein guter Artikel

Zusammenfassung des Artikels

  

importiert in Header-Dateien:

     
  • #importiere die Superklasse, die du vererbst, und die Protokolle, die du implementierst.
  •   
  • Forward-deklarieren Sie alles andere (außer es kommt von einem Framework   mit einem Master-Header).
  •   
  • Versuche alle anderen #imports zu eliminieren.
  •   
  • Deklarieren Sie Protokolle in ihren eigenen Headern, um Abhängigkeiten zu reduzieren.
  •   
  • Zu viele Vorwärtsdeklarationen? Sie haben eine große Klasse.
  •   

importiert in Implementierungsdateien:

     
  • Beseitige craft # Importe, die nicht verwendet werden.
  •   
  • Wenn eine Methode an ein anderes Objekt delegiert wird und zurückgibt, was sie erhält   zurück, versuche, das Objekt weiterzuleiten, anstatt es zu importieren.
  •   
  • Wenn Sie ein Modul hinzufügen, müssen Sie Stufe für Stufe einbeziehen   aufeinander folgenden Abhängigkeiten, können Sie eine Reihe von Klassen haben, die wollen   werde eine Bibliothek. Erstellen Sie es als separate Bibliothek mit einem Master   Header, also kann alles als ein vorgefertigter Chunk eingefügt werden.
  •   
  • Zu viele #Importe? Sie haben eine große Klasse.
  •   
    
homam 12.08.2013 11:16
quelle
3

Wenn ich mich entwickle, habe ich nur drei Dinge im Kopf, die mir nie Probleme bereiten.

  1. Superklassen importieren
  2. Importieren Sie übergeordnete Klassen (wenn Sie Kinder und Eltern haben)
  3. Importieren Sie Klassen außerhalb Ihres Projekts (wie in Frameworks und Bibliotheken)

Für alle anderen Klassen (Unterklassen und untergeordnete Klassen in meinem Projekt self) erkläre ich sie über forward-class.

    
Randy Marsh 18.05.2011 15:42
quelle
2

Wenn Sie versuchen, eine Variable oder eine Eigenschaft in Ihrer Headerdatei zu deklarieren, die Sie noch nicht importiert haben, erhalten Sie eine Fehlermeldung, dass der Compiler diese Klasse nicht kennt.

Dein erster Gedanke ist wahrscheinlich #import it.
Dies kann in einigen Fällen zu Problemen führen.

Zum Beispiel, wenn Sie eine Menge von C-Methoden in der Header-Datei oder Strukturen oder etwas Ähnliches implementieren, weil sie nicht mehrfach importiert werden sollten.

Daher können Sie den Compiler mit @class :

angeben
  

Ich weiß, dass du diese Klasse nicht kennst, aber sie existiert. Es wird importiert oder anderswo implementiert

Im Grunde sagt es dem Compiler, den Mund zu halten und zu kompilieren, obwohl es nicht sicher ist, ob diese Klasse jemals implementiert wird.

Normalerweise verwenden Sie #import in den Dateien .m und @class in den Dateien .h .

    
NSAddict 07.01.2013 12:18
quelle
0

Leiten Sie die Deklaration nur an den Prevent-Compiler weiter, damit dieser Fehler anzeigt.

Der Compiler wird wissen, dass es eine Klasse mit dem Namen gibt, den Sie in Ihrer Header-Datei zum Deklarieren verwendet haben.

    
karthick 22.06.2012 15:12
quelle
0

Der Compiler wird sich nur beschweren, wenn Sie diese Klasse so verwenden, dass der Compiler seine Implementierung kennen muss.

Beispiel:

  1. Das könnte so sein, wenn Sie Ihre Klasse daraus ableiten oder
  2. Wenn Sie ein Objekt dieser Klasse als Membervariable haben (wenn auch selten).

Es wird sich nicht beschweren, wenn Sie es nur als Zeiger verwenden. Natürlich müssen Sie es in die Implementierungsdatei importieren (wenn Sie ein Objekt dieser Klasse instanziieren), da es den Klasseninhalt kennen muss, um ein Objekt zu instanziieren.

HINWEIS: #import ist nicht dasselbe wie #include. Dies bedeutet, dass es keinen Umlaufimport gibt. Import ist eine Art von Anfrage für den Compiler, eine bestimmte Datei nach einigen Informationen zu durchsuchen. Wenn diese Information bereits verfügbar ist, ignoriert der Compiler sie.

Probier's mal, importiere A.h in B.h und B.h in A.h. Es wird keine Probleme oder Beschwerden geben und es wird auch funktionieren.

Wann sollte @class verwendet werden

Sie verwenden @class nur, wenn Sie nicht einmal einen Header in Ihre Kopfzeile importieren wollen. Dies könnte ein Fall sein, in dem Sie nicht einmal wissen wollen, was diese Klasse sein wird. Fälle, in denen Sie noch nicht einmal eine Kopfzeile für diese Klasse haben.

Ein Beispiel dafür könnte sein, dass Sie zwei Bibliotheken schreiben. Eine Klasse, nennen wir sie A, existiert in einer Bibliothek. Diese Bibliothek enthält einen Header aus der zweiten Bibliothek. Dieser Header könnte einen Zeiger auf A haben, muss ihn aber möglicherweise nicht erneut verwenden. Wenn Bibliothek 1 noch nicht verfügbar ist, wird Bibliothek B nicht blockiert, wenn Sie @class verwenden. Wenn Sie jedoch A.h importieren möchten, ist der Fortschritt von Bibliothek 2 blockiert.

    
Deepak G M 18.07.2013 11:15
quelle
0

Denken Sie an @klasse, indem Sie dem Compiler sagen: "Vertraue mir, das existiert".

Denken Sie an #import als Kopieren-Einfügen.

Sie möchten aus verschiedenen Gründen die Anzahl der importierten Importe minimieren. Ohne Nachforschungen fällt mir als Erstes die Kompilierungszeit ein.

Beachten Sie, dass Sie beim Erben von einer Klasse nicht einfach eine Forward-Deklaration verwenden können. Sie müssen die Datei importieren, damit die Klasse, die Sie deklarieren, weiß, wie sie definiert ist.

    
Brandon Mantzey 04.01.2018 16:10
quelle
0

Dies ist ein Beispielszenario, in dem wir @class brauchen.

Wenn Sie ein Protokoll in der Header-Datei erstellen möchten, das einen Parameter mit dem Datentyp derselben Klasse enthält, können Sie @class verwenden. Bitte denken Sie daran, dass Sie Protokolle auch separat deklarieren können, dies ist nur ein Beispiel.

%Vor%     
Sujananth 27.09.2016 13:33
quelle

Tags und Links