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.
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:
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.
Drei einfache Regeln:
#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). Wenn Sie die Deklaration in den Implementierungsdateien weiterleiten, tun Sie wahrscheinlich etwas falsch.
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.
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) ).
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.
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
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:
CGPoint
als Ivar oder Eigenschaft haben, muss der Compiler die Deklaration von CGPoint
. 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.
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.
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).
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.
Wenn ich mich entwickle, habe ich nur drei Dinge im Kopf, die mir nie Probleme bereiten.
Für alle anderen Klassen (Unterklassen und untergeordnete Klassen in meinem Projekt self) erkläre ich sie über forward-class.
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
:
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 .
Der Compiler wird sich nur beschweren, wenn Sie diese Klasse so verwenden, dass der Compiler seine Implementierung kennen muss.
Beispiel:
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.
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.
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%Tags und Links objective-c cocoa cocoa-touch