718 Stimmen

@class vs. #import

Nach meinem Verständnis sollte man eine Forward-Class-Deklaration für den Fall verwenden, dass ClassA einen ClassB-Header einschließen muss und ClassB einen ClassA-Header einschließen muss, um zirkuläre Einschlüsse zu vermeiden. Ich verstehe auch, dass eine #import ist eine einfache ifndef so dass ein Include nur einmal vorkommt.

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

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

Ich würde das wirklich gerne verstehen, im Gegensatz zum einfachen Entfernen der @class Vorwärts-Erklärung und das Auslösen einer #import ein, um die Warnungen des Compilers zum Schweigen zu bringen.

24voto

vent Punkte 963

Ein weiterer Vorteil: Schnelle Zusammenstellung

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

18voto

justin Punkte 103032

Meine Anfrage lautet wie folgt. Wann verwendet man #import und wann verwendet man @class?

Einfache Antwort: Sie #import o #include wenn eine körperliche Abhängigkeit besteht. Ansonsten verwenden Sie Forward-Deklarationen ( @class MONClass , struct MONStruct , @protocol MONProtocol ).

Hier sind einige gängige Beispiele für körperliche Abhängigkeit:

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

Wenn ich eine @class-Deklaration verwende, sehe ich manchmal eine allgemeine Compiler-Warnung wie die folgende: "warning: receiver 'FooController' is a forward class and corresponding @interface may not exist."

Der Compiler ist in dieser Hinsicht eigentlich sehr nachsichtig. Er gibt Hinweise (wie den obigen), aber Sie können Ihren Stapel leicht zerstören, wenn Sie sie ignorieren und nicht #import richtig. Obwohl dies (IMO) der Fall sein sollte, erzwingt der Compiler dies nicht. In ARC ist der Compiler strenger, weil er für die Referenzzählung verantwortlich ist. Was passiert, ist, dass der Compiler auf einen Standardwert zurückgreift, wenn er auf eine unbekannte Methode trifft, die Sie aufrufen. Bei jedem Rückgabewert und Parameter wird angenommen, dass er id . Daher sollten Sie jede Warnung aus Ihren Codebases entfernen, da dies als physische Abhängigkeit angesehen werden sollte. Dies ist vergleichbar mit dem Aufruf einer C-Funktion, die nicht deklariert ist. In C wird davon ausgegangen, dass die Parameter int .

Der Grund, warum Sie Forward-Deklarationen bevorzugen würden, ist, dass Sie Ihre Erstellungszeiten um Faktoren reduzieren können, weil es nur eine minimale Abhängigkeit gibt. Bei Forward-Deklarationen sieht der Compiler, dass es einen Namen gibt, und kann das Programm korrekt parsen und kompilieren, ohne die Klassendeklaration oder alle ihre Abhängigkeiten zu sehen, wenn es keine physische Abhängigkeit gibt. Saubere Builds brauchen weniger Zeit. Inkrementelle Builds nehmen weniger Zeit in Anspruch. Sicherlich müssen Sie am Ende etwas mehr Zeit aufwenden, um sicherzustellen, dass alle Header, die Sie benötigen, für jede Übersetzung sichtbar sind, aber das macht sich schnell durch kürzere Build-Zeiten bezahlt (vorausgesetzt, Ihr Projekt ist nicht winzig).

Wenn Sie #import o #include stattdessen wird dem Compiler viel mehr Arbeit aufgebürdet, als notwendig ist. Außerdem führen Sie komplexe Header-Abhängigkeiten ein. Man kann dies mit einem Brute-Force-Algorithmus vergleichen. Wenn Sie #import Wenn Sie den Quellcode nicht kompilieren, ziehen Sie eine Menge unnötiger Informationen hinein, die viel Arbeitsspeicher, Festplatten-E/A und CPU zum Parsen und Kompilieren der Quellen benötigen.

ObjC ist für eine C-basierte Sprache im Hinblick auf Abhängigkeiten nahezu ideal, da NSObject Typen sind niemals Werte -- NSObject Typen sind immer referenzierte Zeiger. Sie können also mit unglaublich schnellen Kompilierzeiten auskommen, wenn Sie die Abhängigkeiten Ihres Programms angemessen strukturieren und nach Möglichkeit weiterleiten, da nur sehr wenige physische Abhängigkeiten erforderlich sind. Sie können auch Eigenschaften in den Klassenerweiterungen deklarieren, um die Abhängigkeiten weiter zu minimieren. Das ist ein enormer Vorteil für große Systeme - Sie kennen den Unterschied, den es macht, wenn Sie jemals eine große C++-Codebasis entwickelt haben.

Daher empfehle ich, wenn möglich Vorwärtsbewegungen zu verwenden, und dann #import wenn eine körperliche Abhängigkeit besteht. Wenn Sie die Warnung oder eine andere sehen, die körperliche Abhängigkeit andeutet - reparieren Sie sie alle. Die Korrektur besteht darin #import in Ihrer Implementierungsdatei.

Wenn Sie Bibliotheken erstellen, werden Sie wahrscheinlich einige Schnittstellen als Gruppe klassifizieren, in diesem Fall würden Sie #import diese Bibliothek, 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 bei Bedarf für Sie handhaben - wenn sie eine Funktion einführen, können sie die Auswirkungen auf Ihre Builds minimieren.

11voto

Bruce Goodwin Punkte 208

Ich sehe eine Menge "Mach es so", aber ich sehe keine Antworten auf die Frage "Warum?".

Also: Warum sollten Sie @class in Ihrem Header und #import nur in Ihrer Implementierung verwenden? Sie verdoppeln Ihre Arbeit, wenn Sie @class und #import die ganze Zeit. Es sei denn, Sie nutzen die Vererbung. In diesem Fall müssen Sie für eine einzige @Klasse mehrfach #importieren. 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.

Dass dieselbe Datei mehrmals importiert werden kann, ist aufgrund der Natur von #import kein Problem. Auch die Kompilierleistung ist nicht wirklich ein Problem. Wenn es wäre, würden wir nicht #importing Cocoa/Cocoa.h oder dergleichen in so ziemlich jeder Header-Datei, die wir haben.

7voto

Anshuman Mishra Punkte 189

Wenn wir dies tun

@interface Class_B : Class_A

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

wenn wir dies tun

#import ....
@class Class_A
@interface Class_B

hier sagen wir, dass wir die Klasse_A in unserem Programm verwenden, aber wenn wir die Variablen der Klasse_A in der Klasse_B verwenden wollen, müssen wir die Klasse_A in der .m-Datei #importieren (ein Objekt erstellen und seine Funktionen und Variablen verwenden).

5voto

Homam Punkte 568

Für zusätzliche Informationen über Dateiabhängigkeiten & #import & @class siehe hier:

http://qualitycoding.org/file-dependencies/ es ist ein guter Artikel

Zusammenfassung des Artikels

Importe in Header-Dateien:

  • Importieren Sie die Superklasse, die Sie erben, und die Protokolle, die Sie implementieren.

  • Alles andere ist weiterzudeklarieren (es sei denn, es kommt von einem Framework mit einem Master-Header).
  • Versuchen Sie, alle anderen #Einfuhren zu eliminieren.
  • Deklarieren Sie Protokolle in ihren eigenen Headern, um Abhängigkeiten zu reduzieren.
  • Zu viele Vorwärtsdeklarationen? Sie haben eine Große Klasse.

Importe in Implementierungsdateien:

  • Beseitigen Sie überflüssige #Importe, die nicht verwendet werden.
  • Wenn eine Methode an ein anderes Objekt delegiert und das zurückgibt, was sie erhält zurückgibt, versuchen Sie, dieses Objekt weiterzudeklarieren, anstatt es zu #importieren.
  • Wenn Sie durch die Aufnahme eines Moduls gezwungen sind, Ebene für Ebene von Abhängigkeiten einzubinden, haben Sie vielleicht einen Satz von Klassen, der eine Bibliothek werden will. Bauen Sie sie als separate Bibliothek mit einem Master Header, so dass alles als ein einziges vorgefertigtes Stück eingebracht werden kann.
  • Zu viele #Importe? Sie haben eine Große Klasse.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X