Nagłówki podzielone na segmenty

Pliki nagłówków generowane przez J2ObjC są podzielone na segmenty, które można umieszczać pojedynczo. Dla każdego przetłumaczonego typu języka Java tworzony jest jeden segment, dlatego każda klasa wewnętrzna ma własny segment. Makra procesora wstępnego służą do informowania kompilatora o tym, że ma odczytać dany segment z nagłówkiem.Nagłówki podzielone na segmenty rozwiązują problem uwzględniania cykli w nagłówkach wygenerowanych przez J2ObjC, co zostało opisane szczegółowo poniżej.

Wymagana wiedza

  • Aby uwzględnić nagłówki wygenerowane przez J2ObjC, użyj #include zamiast #import.
    • Używanie atrybutu #import z nagłówkami podzielonymi na segmenty jest problematyczne, ponieważ kompilator pominie odczytywanie nagłówka, jeśli już go odczytał. Nagłówek jest jednak podzielony na segmenty, więc mógł nie zostać w pełni przeanalizowany przez kompilator za pierwszym razem.
  • Podzielone nagłówki można wyłączyć za pomocą flagi --no-segmented-headers.

Ulotka zawiera

Aby rozpoznać niezbędne informacje o typie, pliki nagłówka wygenerowane przez J2ObjC muszą korzystać z deklaracji typu „zawiera” i „Przekaż dalej”. Deklaracje Forward są używane w miarę możliwości, ale są niezbędne w przypadku rozszerzania lub implementowania typów, ponieważ kompilator wymaga pełnej deklaracji typu.

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

Naturalnym rozwiązaniem tego problemu może być wysłanie osobnego pliku nagłówka dla każdego typu języka Java występującego w pliku .java. J2ObjC zostało jednak zaprojektowane do użytku jako narzędzie do kompilacji, a każdy dobry system kompilacji wymaga przewidywalnych danych wyjściowych dla każdego wejścia. Oznacza to, że każdy plik .java musi zawierać dokładnie 1 plik .h i 1 .m.

Przykład

Foo.java:

class Foo extends Bar {}

Bar.java:

class Bar {
  static class Baz extends Foo {}
}

Foo.h (bez podziału 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 (nieposegmentowany):

#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 Foo.h zawiera elementy Bar.h, a Bar.h – Foo.h. W efekcie nagłówki nie skompilowają się:

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

Poniżej znajdziesz podzielone na segmenty wersje Foo.h i Bar.h, które skompilują się bez błędów.

Foo.h (podzielone 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 (podzielone na 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")