Segmentierte Header

Von J2ObjC generierte Headerdateien sind in Segmente unterteilt und können jeweils nur für ein Segment eingefügt werden. Für jeden übersetzten Java-Typ wird ein Segment erstellt. Daher hat jede innere Klasse ein eigenes Segment. Präprozessor-Makros werden verwendet, um den Compiler anzuweisen, ein bestimmtes Segment nur zu lesen, wenn der Header enthalten ist.Segmentierte Header lösen das Problem der Einschlusszyklen in J2ObjC-generierten Headern, die unten ausführlich beschrieben werden.

Wissenswertes

  • Verwenden Sie #include anstelle von #import, um J2ObjC-generierte Header einzuschließen.
    • Die Verwendung von #import mit segmentierten Headern ist problematisch, da der Compiler das Lesen des Headers überspringt, wenn er ihn bereits gesehen hat. Da der Header jedoch segmentiert ist, wurde er möglicherweise beim ersten Mal nicht vollständig vom Compiler geparst.
  • Segmentierte Header können mit dem Flag --no-segmented-headers deaktiviert werden.

Inklusive Rundschreiben

Von J2ObjC generierte Headerdateien müssen Include- bzw. Forward-Deklarationen verwenden, um die erforderlichen Typinformationen aufzulösen. Forward-Deklarationen werden so oft wie möglich verwendet. „Includes“ sind jedoch für Typen erforderlich, die erweitert oder implementiert werden, da der Compiler die vollständige Typdeklaration benötigt.

Es ist möglich, Include-Zyklen in J2ObjC-generierten Header-Dateien zu generieren. Um einen solchen Zyklus zu erhalten, benötigen wir eine Klasse in Datei A, die eine Klasse in Datei B erweitert, und eine Klasse in Datei B, die eine Klasse in Datei A erweitert. Dies ist ein unwahrscheinliches Szenario, kommt aber in der Codebasis von Guava (und anderswo) vor.

Eine natürliche Lösung für dieses Problem könnte darin bestehen, für jeden Java-Typ, der in einer .java-Datei auftritt, eine separate Headerdatei auszugeben. J2ObjC ist jedoch als Build-Tool gedacht und jedes gute Build-System stützt sich auf vorhersehbare Ausgaben für jede Eingabe. Daher muss jede Java-Datei genau eine H- und eine M-Datei erzeugen.

Beispiel

Foo.java:

class Foo extends Bar {}

Bar.java:

class Bar {
  static class Baz extends Foo {}
}

Foo.h (nicht segmentiert):

#ifndef _Foo_H_
#define _Foo_H_

#include "Bar.h"
#include "J2ObjC_header.h"

@interface Foo : Bar
- (instancetype)init;
@end

#endif // _Foo_H_

Balken.h (nicht segmentiert):

#ifndef _Bar_H_
#define _Bar_H_

#include "Foo.h"
#include "J2ObjC_header.h"

@interface Bar : NSObject
- (instancetype)init;
@end

@interface Bar_Baz : Foo
- (instancetype)init;
@end

#endif // _Bar_H_

Beachten Sie, dass Foo.h Bar.h enthält und Bar.h Foo.h enthält. Daher können diese Header nicht kompiliert werden:

../dist/j2objcc -c Foo.m
In file included from Foo.m:6:
In file included from ./Bar.h:9:
./Foo.h:12:18: error: cannot find interface declaration for 'Bar', superclass of 'Foo'
@interface Foo : Bar
~~~~~~~~~~~~~~   ^

Unten siehst du die segmentierten Versionen von Foo.h und Bar.h, die ohne Fehler kompiliert werden.

Foo.h (segmentiert):

#include "J2ObjC_header.h"

#pragma push_macro("Foo_INCLUDE_ALL")
#if Foo_RESTRICT
#define Foo_INCLUDE_ALL 0
#else
#define Foo_INCLUDE_ALL 1
#endif
#undef Foo_RESTRICT

#if !defined (_Foo_) && (Foo_INCLUDE_ALL || Foo_INCLUDE)
#define _Foo_

#define Bar_RESTRICT 1
#define Bar_INCLUDE 1
#include "Bar.h"

@interface Foo : Bar
- (instancetype)init;
@end

#endif

#pragma pop_macro("Foo_INCLUDE_ALL")

Balken.h (segmentiert):

#include "J2ObjC_header.h"

#pragma push_macro("Bar_INCLUDE_ALL")
#if Bar_RESTRICT
#define Bar_INCLUDE_ALL 0
#else
#define Bar_INCLUDE_ALL 1
#endif
#undef Bar_RESTRICT

#if !defined (_Bar_) && (Bar_INCLUDE_ALL || Bar_INCLUDE)
#define _Bar_

@interface Bar : NSObject
- (instancetype)init;
@end

#endif

#if !defined (_Bar_Baz_) && (Bar_INCLUDE_ALL || Bar_Baz_INCLUDE)
#define _Bar_Baz_

#define Foo_RESTRICT 1
#define Foo_INCLUDE 1
#include "Foo.h"

@interface Bar_Baz : Foo
- (instancetype)init;
@end

#endif

#pragma pop_macro("Bar_INCLUDE_ALL")