Сегментированные заголовки

Файлы заголовков, сгенерированные J2ObjC, разделены на сегменты и могут включаться по одному сегменту за раз. Для каждого переведенного типа Java создается один сегмент, поэтому каждый внутренний класс будет иметь свой собственный сегмент. Макросы препроцессора используются для указания компилятору читать только определенный сегмент при включении заголовка. Сегментированные заголовки решают проблему включения циклов в сгенерированные заголовки J2ObjC, подробно описанную ниже.

Что тебе нужно знать

  • Используйте #include вместо #import , чтобы включить заголовки, созданные J2ObjC.
    • Использование #import с сегментированными заголовками проблематично, поскольку компилятор пропустит чтение заголовка, если он уже видел его. Но поскольку заголовок сегментирован, возможно, он не был полностью проанализирован компилятором в первый раз.
  • Сегментированные заголовки можно отключить с помощью флага --no-segmented-headers .

Циркуляр включает

Файлы заголовков, сгенерированные J2ObjC, должны использовать объявления include и Forward для разрешения необходимой информации о типе. Упреждающие объявления используются настолько часто, насколько это возможно, однако включения необходимы для расширяемых или реализуемых типов, поскольку компилятор требует полного объявления типа.

В файлах заголовков, сгенерированных J2ObjC, можно генерировать циклы включения. Чтобы получить такой цикл, нам нужен класс в файле A, который расширяет класс в файле B, и класс в файле B, который расширяет класс в файле A. Это маловероятный сценарий, но он действительно встречается в кодовой базе Guava (и в других местах). ).

Естественным решением этой проблемы могло бы стать создание отдельного файла заголовка для каждого типа Java, встречающегося в файле .java. Но J2ObjC предназначен для использования в качестве инструмента сборки, и любая хорошая система сборки опирается на предсказуемые выходные данные для каждого входа. Это означает, что необходимо, чтобы каждый файл .java создавал ровно один файл .h и один файл .m.

Пример

Фу.java:

class Foo extends Bar {}

Бар.java:

class Bar {
  static class Baz extends Foo {}
}

Foo.h (не сегментированный):

#ifndef _Foo_H_
#define _Foo_H_

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

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

#endif // _Foo_H_

Bar.h (не сегментированный):

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

Обратите внимание, что Foo.h включает Bar.h, а Bar.h включает Foo.h. В результате эти заголовки не могут скомпилироваться:

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

Ниже приведены сегментированные версии Foo.h и Bar.h, которые скомпилируются без ошибок.

Foo.h (сегментированный):

#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 (сегментированный):

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