Nach meinem Verständnis sollte eine Forward-Class-Deklaration verwendet werden, wenn ClassA einen ClassB-Header und ClassB einen ClassA-Header enthalten muss, um kreisförmige Einschlüsse zu vermeiden. Ich verstehe auch, dass an #import
ein einfaches ist, ifndef
so dass ein Include nur einmal vorkommt.
Meine Anfrage lautet: Wann benutzt man #import
und wann benutzt man @class
? Wenn ich eine @class
Deklaration verwende, wird manchmal eine allgemeine Compiler-Warnung wie die folgende angezeigt:
warning: receiver 'FooController' is a forward class and corresponding @interface may not exist.
Würde das wirklich gerne verstehen, anstatt nur die @class
Forward-Deklaration zu entfernen und #import
die Warnungen, die der Compiler mir gibt, zum Schweigen zu bringen.
quelle
Antworten:
Wenn Sie diese Warnung sehen:
Sie müssen
#import
die Datei, aber Sie können dies in Ihrer Implementierungsdatei (.m) tun und die@class
Deklaration in Ihrer Header-Datei verwenden.@class
Entfernt (normalerweise) nicht die Notwendigkeit von#import
Dateien, sondern rückt die Anforderung nur näher an den Ort, an dem die Informationen nützlich sind.Zum Beispiel
Wenn Sie sagen
@class MyCoolClass
, weiß der Compiler, dass er möglicherweise Folgendes sieht:Es muss sich um nichts anderes kümmern als
MyCoolClass
um eine gültige Klasse, und es sollte Platz für einen Zeiger darauf reservieren (wirklich nur einen Zeiger). Somit reicht in Ihrem Header@class
90% der Zeit aus.Wenn Sie jedoch jemals
myObject
Mitglieder erstellen oder darauf zugreifen müssen, müssen Sie den Compiler über diese Methoden informieren. An diesem Punkt (vermutlich in Ihrer Implementierungsdatei) müssen Sie#import "MyCoolClass.h"
dem Compiler zusätzliche Informationen mitteilen, die über "Dies ist eine Klasse" hinausgehen.quelle
@class
etwas in Ihrer.h
Datei haben, es aber#import
in der .m vergessen , versuchen, auf eine Methode für das@class
ed-Objekt zuzugreifen , und Warnungen erhalten wie :warning: no -X method found
.Drei einfache Regeln:
#import
die Superklasse und übernommene Protokolle in Header-Dateien (.h
Dateien).#import
Alle Klassen und Protokolle, an die Sie in der Implementierung Nachrichten senden (.m
Dateien).Wenn Sie eine Deklaration in den Implementierungsdateien weiterleiten, machen Sie wahrscheinlich etwas falsch.
quelle
Lesen Sie die Dokumentation zur Objective-C-Programmiersprache in ADC
Unter dem Abschnitt Definieren einer Klasse | Klassenschnittstelle beschreibt, warum dies getan wird:
Ich hoffe das hilft.
quelle
Verwenden Sie bei Bedarf eine Vorwärtsdeklaration in der Header-Datei und
#import
die Header-Dateien 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 auf eine Klasse in Ihrer Header-Datei verweisen müssen, verwenden Sie auch eine Forward-Deklaration.Die Ausnahme ist, dass Sie
#import
eine Klasse oder ein formales Protokoll, von dem Sie erben, in Ihrer Header-Datei erben sollten (in diesem Fall müssten Sie es nicht in die Implementierung importieren).quelle
Die gängige Praxis ist die Verwendung von @class in Header-Dateien (Sie müssen die Superklasse jedoch noch # importieren) und #import in Implementierungsdateien. Dies vermeidet kreisförmige Einschlüsse und funktioniert einfach.
quelle
#import
"Entspricht der Anweisung #include von C, stellt jedoch sicher, dass dieselbe Datei nie mehr als einmal enthalten ist." Demnach#import
kümmert sich das um kreisförmige Einschlüsse,@class
Richtlinien helfen dabei nicht besonders.Ein weiterer Vorteil: Schnelle Kompilierung
Wenn Sie eine Header-Datei einfügen, wird die aktuelle Datei bei jeder Änderung ebenfalls kompiliert. Dies ist jedoch nicht der Fall, wenn der Klassenname als enthalten ist
@class name
. Natürlich müssen Sie den Header in die Quelldatei aufnehmenquelle
Einfache Antwort: Sie
#import
oder#include
wenn es eine physische Abhängigkeit gibt. Andernfalls verwenden Sie Vorwärtsdeklarationen (@class MONClass
,struct MONStruct
,@protocol MONProtocol
).Hier sind einige häufige Beispiele für körperliche Abhängigkeit:
CGPoint
als ivar oder Eigenschaft haben, muss der Compiler die Deklaration von sehenCGPoint
.Der Compiler ist in dieser Hinsicht eigentlich sehr nachsichtig. Es werden Hinweise (wie oben) gelöscht, aber Sie können Ihren Stapel leicht in den Papierkorb werfen, wenn Sie sie ignorieren und nicht
#import
richtig. Obwohl dies der Fall sein sollte (IMO), erzwingt der Compiler dies nicht. In ARC ist der Compiler strenger, da er für die Referenzzählung verantwortlich ist. Der Compiler greift auf eine Standardeinstellung zurück, wenn er auf eine unbekannte Methode stößt, die Sie aufrufen. Jeder Rückgabewert und Parameter wird angenommenid
. Daher sollten Sie jede Warnung aus Ihren Codebasen entfernen, da dies als physische Abhängigkeit betrachtet werden sollte. Dies ist analog zum Aufrufen einer C-Funktion, die nicht deklariert ist. Mit C werden Parameter angenommenint
.Der Grund, warum Sie Forward-Deklarationen bevorzugen, besteht darin, dass Sie Ihre Erstellungszeiten um Faktoren reduzieren können, da nur eine minimale Abhängigkeit besteht. Bei Vorwärtsdeklarationen sieht der Compiler, dass es einen Namen gibt, und kann das Programm korrekt analysieren und kompilieren, ohne die Klassendeklaration oder alle ihre 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 etwas mehr Zeit damit verbringen, sicherzustellen, dass alle benötigten Header für jede Übersetzung sichtbar sind. Dies zahlt sich jedoch in kürzeren Erstellungszeiten schnell aus (vorausgesetzt, Ihr Projekt ist nicht winzig).
Wenn Sie
#import
oder#include
stattdessen verwenden, werfen Sie viel mehr Arbeit auf den Compiler als nötig. Sie führen auch komplexe Header-Abhängigkeiten ein. Sie können dies mit einem Brute-Force-Algorithmus vergleichen. Wenn Sie dies#import
tun, ziehen Sie Tonnen unnötiger Informationen ein, die viel Speicher, Festplatten-E / A und CPU erfordern, um die Quellen zu analysieren und zu kompilieren.ObjC ist in Bezug auf die Abhängigkeit nahezu ideal für eine C-basierte Sprache, da
NSObject
Typen niemals Werte sind -NSObject
Typen sind immer Zeiger mit Referenzzählung. Sie können also mit unglaublich schnellen Kompilierungszeiten davonkommen, 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.Daher empfehle ich, wenn möglich vorwärts zu verwenden und dann
#import
dorthin , wo physische Abhängigkeit besteht. Wenn Sie die eine oder andere Warnung sehen, die physische Abhängigkeit impliziert, beheben Sie sie alle. Das Update befindet sich#import
in Ihrer Implementierungsdatei.Während Sie Bibliotheken erstellen, werden Sie wahrscheinlich einige Schnittstellen als Gruppe klassifizieren. In diesem Fall würden Sie die
#import
Bibliothek verwenden, in der physische Abhängigkeit eingeführt wird (z#import <AppKit/AppKit.h>
. B. ). Dies kann zu Abhängigkeiten führen, aber die Bibliotheksverwalter können häufig die physischen Abhängigkeiten für Sie nach Bedarf behandeln. Wenn sie eine Funktion einführen, können sie die Auswirkungen auf Ihre Builds minimieren.quelle
NSObject types are never values -- NSObject types are always reference counted pointers.
nicht ganz wahr. Blöcke werfen eine Lücke in Ihre Antwort und sagen nur.Ich sehe viele "Mach es so", aber ich sehe keine Antworten auf "Warum?"
Also: Warum sollten Sie @class in Ihrem Header und #import nur in Ihrer Implementierung verwenden? Sie verdoppeln Ihre Arbeit, indem Sie die ganze Zeit @class und #importieren müssen. Es sei denn, Sie nutzen die Vererbung. In diesem Fall # importieren Sie mehrmals für eine einzelne @ -Klasse. 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 derselben Datei ist aufgrund der Art von #import kein Problem. Das Kompilieren der Leistung ist auch kein wirkliches 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.
quelle
wenn wir das tun
Das heißt, wir erben die Klasse_A in die Klasse_B. In der Klasse_B können wir auf alle Variablen der Klasse_A zugreifen.
wenn wir das tun
Hier sagen wir, dass wir Class_A in unserem Programm verwenden, aber wenn wir die Class_A-Variablen in Class_B verwenden möchten, müssen wir Class_A in die .m-Datei importieren (ein Objekt erstellen und dessen Funktion und Variablen verwenden).
quelle
Weitere Informationen zu Dateiabhängigkeiten und #import & @class finden Sie hier:
http://qualitycoding.org/file-dependencies/ Es ist ein guter Artikel
Zusammenfassung des Artikels
quelle
Wenn ich mich entwickle, habe ich nur drei Dinge im Sinn, die mir nie Probleme bereiten.
Für alle anderen Klassen (Unterklassen und untergeordnete Klassen in meinem Projekt selbst) deklariere ich sie über die Vorwärtsklasse.
quelle
Wenn Sie versuchen, eine Variable oder eine Eigenschaft in Ihrer Header-Datei zu deklarieren, die Sie noch nicht importiert haben, wird eine Fehlermeldung angezeigt, dass der Compiler diese Klasse nicht kennt.
Ihr erster Gedanke ist es wahrscheinlich
#import
.Dies kann in einigen Fällen zu Problemen führen.
Zum Beispiel, wenn Sie eine Reihe von C-Methoden in der Header-Datei oder in Strukturen oder Ähnlichem implementieren, da diese nicht mehrmals importiert werden sollten.
Daher können Sie dem Compiler Folgendes mitteilen
@class
:Grundsätzlich wird der Compiler angewiesen, die Klappe zu halten und zu kompilieren, obwohl nicht sicher ist, ob diese Klasse jemals implementiert wird.
Sie werden normalerweise
#import
in den .m- und@class
in den .h- Dateien verwenden.quelle
Leiten Sie die Deklaration nur an den Compiler weiter, damit dieser keinen Fehler anzeigt.
Der Compiler weiß, dass es eine Klasse mit dem Namen gibt, den Sie in Ihrer Header-Datei zum Deklarieren verwendet haben.
quelle
Der Compiler wird sich nur beschweren, wenn Sie diese Klasse so verwenden, dass der Compiler ihre Implementierung kennen muss.
Ex:
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 nichts gibt, was als zirkulärer Import bezeichnet wird. Import ist eine Art Aufforderung an den Compiler, in einer bestimmten Datei nach Informationen zu suchen. Wenn diese Informationen bereits verfügbar sind, ignoriert der Compiler sie.
Versuchen Sie dies einfach, importieren Sie Ah in Bh und Bh in Ah. Es wird keine Probleme oder Beschwerden geben und es wird auch gut funktionieren.
Wann wird @class verwendet?
Sie verwenden @class nur, wenn Sie nicht einmal einen Header in Ihren Header importieren möchten. Dies könnte ein Fall sein, in dem Sie nicht einmal wissen möchten, wie diese Klasse aussehen wird. Fälle, in denen Sie möglicherweise noch nicht einmal einen Header für diese Klasse haben.
Ein Beispiel hierfür könnte sein, dass Sie zwei Bibliotheken schreiben. Eine Klasse, nennen wir es A, existiert in einer Bibliothek. Diese Bibliothek enthält einen Header aus der zweiten Bibliothek. Dieser Header hat möglicherweise einen Zeiger auf A, muss ihn jedoch möglicherweise nicht verwenden. Wenn Bibliothek 1 noch nicht verfügbar ist, wird Bibliothek B nicht blockiert, wenn Sie @class verwenden. Wenn Sie jedoch Ah importieren möchten, wird der Fortschritt von Bibliothek 2 blockiert.
quelle
Stellen Sie sich @class so vor, als würde es dem Compiler sagen "Vertrau mir, das gibt es".
Stellen Sie sich #import als Copy-Paste vor.
Sie möchten die Anzahl Ihrer Importe aus verschiedenen Gründen minimieren. Ohne Forschung fällt mir als Erstes ein, dass die Kompilierungszeit verkürzt wird.
Beachten Sie, dass Sie beim Erben von einer Klasse nicht einfach eine Vorwärtsdeklaration verwenden können. Sie müssen die Datei importieren, damit die Klasse, die Sie deklarieren, weiß, wie sie definiert ist.
quelle
Dies ist ein Beispielszenario, in dem wir @class benötigen.
Überlegen Sie, ob Sie @class verwenden möchten, wenn Sie ein Protokoll in der Header-Datei erstellen möchten, das einen Parameter mit einem Datentyp derselben Klasse enthält. Bitte denken Sie daran, dass Sie Protokolle auch separat deklarieren können. Dies ist nur ein Beispiel.
quelle