Cocoa: Was ist der Unterschied zwischen dem Import in der Kopfzeile und dem Import in der Hauptdatei?

7

Ich habe keine Ahnung warum, aber gelegentlich habe ich einige Kompilierungsfehler behoben, vor allem

%Vor%

durch Verschieben von #import "someClass.h" aus der .h-Datei in die .m-Datei. Dies hat auch mit ein paar anderen Problemen funktioniert, die ich angetroffen habe, die (auf mysteriöse Weise aus meiner Sicht) mit Kopfzeilen zusammenhängen.

Einige oberflächliche googeln hat die Antwort "Nie Header in Header-Datei importieren" und das ist, wo der Rat hört auf.

Entweder habe ich das komplett erfunden, oder ich habe die Gewohnheit von irgendwoher aufgenommen, aber ich dachte, der Header wäre der, wo Header importiert werden sollten. Natürlich nicht, aber kann mir jemand erklären, warum das so ist, und was ist der bevorzugte Weg, Header zu importieren?

    
gargantuan 15.12.2009, 13:46
quelle

3 Antworten

10

Sofern Sie nicht von der Klasse, die Sie einbeziehen, vererben, sollten Sie keine Header in die Header einfügen. Wenn Sie ein Objekt als Schnittstellenvariable einfügen müssen, sollten Sie% verwenden. stattdessen co_de% directive; Das sagt dem Compiler, dass sich der Bezeichner auf eine Klasse bezieht.

Importieren Sie stattdessen Header nur in Implementierungsdateien. Der Compiler weiß, dass Ihre Instanzvariablen Zeiger auf Objekte sind, aber er kennt die Details des Objekts nicht, wenn er den Header analysiert. Alles was es wissen muss ist, dass es eine Klasse ist. Der Compiler kann dann die Methoden der Klasse beim Analysieren der Implementierungsdatei sehen. An diesem Punkt benötigt es die Klasse, um zu überprüfen, ob sie auf die Nachrichten reagiert, die Sie senden.

Update: Ich wollte meine Antwort aktualisieren, um auf einige spätere Fragen zu antworten, aber Rob Napier hat ein gutes Follow-up .

    
John Feminella 15.12.2009, 13:52
quelle
13

John gibt einen guten Rat, aber hier ist ein bisschen mehr Hintergrund über das Warum, warum und Ausnahmen.

Es gibt zwei Ziele, die es vermeiden, Header in Header zu importieren: verbesserte inkrementelle Build-Zeiten und die Vermeidung zirkulärer Abhängigkeiten. Wenn Sie A.h in B.h importieren und B.h in C.h importieren, müssen Sie jedes Mal, wenn Sie etwas in A.h ändern, C.m erneut kompilieren, auch wenn C.m keine davon verwendet die Dinge in A.h definiert. Dies kann zu wirklich schrecklichen und unnötigen Build-Churn führen, besonders wenn sich die Header häufig ändern (wie es in den frühen Entwicklungsstadien üblich ist).

Das erste Ziel ist lobenswert, aber für kleine Projekte, wen interessiert das? Sie haben einen Quad-Core-Pro und ein kompletter Build dauert ein paar Minuten, oder? Aber Sie müssen sich immer noch um das zweite Problem kümmern: zirkuläre Abhängigkeiten. A.h referenziert Klasse B und B.h referenziert Klasse A . Dies kann sehr oft passieren und kann sich unschuldig in ein System einschleichen. Ein Auflistungsobjekt verweist möglicherweise auf den Typ der darin enthaltenen Objekte, und die Objekte verweisen möglicherweise auf den Typ des Auflistungsobjekts. Alles was es braucht, ist eine einzelne Referenz wegen einer Methode, die diesen Typ übernimmt oder zurückgibt. Wenn Sie Header haben, die andere Header importieren, nähert sich die Wahrscheinlichkeit, dass dies geschieht, schnell der Einheit an. Sie landen mit rekursiven Importen und die Kompilierungsfehler können überwältigend sein. "Ich weiß das Typdef definiert ist! Es ist genau dort! Es ist importiert!" Es wurde jedoch noch nicht analysiert, als Sie diesen Header importiert haben. Dies ist der Grund für den oben genannten Fehler.

Aus diesem Grund sollten Sie, auch wenn Ihnen die Build-Zeiten nicht so wichtig sind (wie Sie sollten), vermeiden, Header in Header zu importieren ... außer ....

Einige Header, die Sie importieren . Deine Oberklasse natürlich. Dateien, die ein von Ihnen implementiertes @protocol oder typedef definieren. Also ja, du musst diese einbeziehen.

Und was ist mit Systemheadern? Nun, sie werden niemals Abwanderungen verursachen, und offensichtlich werden sie keine rekursiven Importe verursachen, also sind sie in Ordnung. Ich rate Leuten davon ab, @class forward-Deklarationen für Dinge in Systemheadern zu verwenden. Es erzeugt zusätzliche Arbeit für den Benutzer Ihrer Kopfzeile ohne Wert. Für eine gute Header-Hygiene denken Sie bitte daran, System-Header in & lt; eckigen Klammern & gt; und Ihre Kopfzeilen in "Anführungszeichen".

Es ist also keine triviale Frage, aber die einfache Regel lautet: Vermeiden Sie es, Benutzer-Header in andere Benutzer-Header zu importieren, wann immer der Compiler es Ihnen erlaubt.

    
Rob Napier 15.12.2009 14:37
quelle
0

Sie fügen Ihren Header nicht nur in Ihre Implementierungsdatei ein, sondern auch in andere Dateien, die diese Klasse verwenden. Wenn Sie alle Abhängigkeiten in die Kopfzeile einschließen, werden alle anderen Dateien einschließlich der Kopfzeile nur für die Verwendung der spezifischen Klasse, die sie definiert, auch alle Abhängigkeiten enthalten.

Dies ist nicht nur laut, sondern führt zu Problemen, wenn Sie sich selbst auf Klassen beziehen. Jede Klasse müsste vor der anderen importiert werden, was dazu führt, dass zuerst eine importiert wird und dann die andere Klasse nicht gefunden werden kann. Dies führt zu der kryptischen Fehlermeldung, die Sie gepostet haben, was bedeutet, dass der Compiler den someClass -Typ nicht finden kann.

Durch das Verschieben Ihrer Importe in Ihre Implementierungsdatei und das Weiterleiten von Klassen und Typen in Ihrer Kopfzeile (mit @class , @protocol usw.) können Sie diese Probleme vermeiden.

    
Adrian 15.12.2009 14:38
quelle

Tags und Links