Nagłówki podzielone na segmenty

Generowane przez J2ObjC pliki nagłówków są podzielone na segmenty, które można uwzględniać po jednym naraz. Dla każdego przetłumaczonego typu Java tworzy się 1 segment, więc każda klasa wewnętrzna będzie miała swój własny segment. Makrony preprocesora służą do informowania kompilatora, aby podczas uwzględniania nagłówka odczytywał tylko określony segment. Nagłówki podzielone na segmenty rozwiązują problem cykli include w nagłówkach wygenerowanych przez J2ObjC, który został szczegółowo opisany w tym dokumencie.

Podstawowe informacje

  • Aby uwzględnić nagłówki wygenerowane przez J2ObjC, użyj wartości #include zamiast #import.
    • Używanie #import w przypadku nagłówków segmentowanych jest problematyczne, ponieważ kompilator pominie odczytywanie nagłówka, jeśli już go widział. Jednak ponieważ nagłówek jest podzielony na segmenty, kompilator może nie przeanalizować go w pełni za pierwszym razem.
  • Nagłówki podzielone na segmenty można wyłączyć za pomocą parametru --no-segmented-headers.

Ulotka z dodatkowymi informacjami

Generowane przez J2ObjC pliki nagłówków muszą używać deklaracji include i forward, aby rozwiązywać niezbędne informacje o typie. Deklaracje wsteczne są używane w jak największym stopniu, ale w przypadku rozszerzonych lub implementowanych typów wymagane są dyrektywy include, ponieważ kompilator wymaga pełnej deklaracji typu.

W plikach nagłówków wygenerowanych przez J2ObjC można wygenerować cykle include. Aby uzyskać taki cykl, potrzebujemy klasy w pliku A, która rozszerza klasę w pliku B, oraz klasy w pliku B, która rozszerza klasę w pliku A. Jest to mało prawdopodobny scenariusz, ale występuje w kodzie Guavy (i w innych miejscach).

Naturalnym rozwiązaniem tego problemu może być wygenerowanie osobnego pliku nagłówka dla każdego typu Java napotykanego w pliku .java. Jednak J2ObjC jest przeznaczony do użycia jako narzędzie do kompilacji, a każdy dobry system kompilacji opiera się na przewidywalnych danych wyjściowych dla każdego wejścia. Oznacza to, że każdy plik .java musi generować dokładnie 1 plik .h i 1 plik .m.

Przykład

Foo.java:

class Foo extends Bar {}

Bar.java:

class Bar {
  static class Baz extends Foo {}
}

Foo.h (nie podzielony na segmenty):

#ifndef _Foo_H_
#define _Foo_H_

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

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

#endif // _Foo_H_

Bar.h (bez podziału na segmenty):

#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_

Zwróć uwagę, że plik Foo.h zawiera plik Bar.h, a ten ostatni zawiera plik Foo.h. W rezultacie te nagłówki nie zostaną skompilowane:

../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
~~~~~~~~~~~~~~   ^

Posegmentowane wersje plików Foo.h i Bar.h, które skompilują się bez błędów:

Foo.h (podział na segmenty):

#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")

Bar.h (segmenty):

#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")