ヘッダーの分割

J2ObjC によって生成されたヘッダー ファイルはセグメントに分割され、一度に 1 つのセグメントを含めることができます。翻訳された Java 型ごとに 1 つのセグメントが作成されるため、各内部クラスに独自のセグメントが割り当てられます。プリプロセッサ マクロは、ヘッダーを含めるときに特定のセグメントのみを読み取るようにコンパイラに指示するために使用されます。セグメント化されたヘッダーは、J2ObjC によって生成されたヘッダー内のインクルード サイクルの問題を解決します。この問題については、このドキュメントで詳しく説明します。

ご注意いただきたい点

  • J2ObjC によって生成されたヘッダーを含めるには、#import ではなく #include を使用します。
    • セグメント化されたヘッダーで #import を使用すると問題が発生します。これは、コンパイラがヘッダーをすでに認識している場合、ヘッダーの読み取りをスキップするためです。ただし、ヘッダーはセグメント化されているため、コンパイラで最初に完全に解析されていない可能性があります。
  • セグメント化されたヘッダーは、--no-segmented-headers フラグで無効にできます。

チラシの内容

J2ObjC で生成されたヘッダー ファイルでは、必要な型情報を解決するためにインクルードと前方宣言を使用する必要があります。可能な限り前方宣言を使用しますが、コンパイラは完全な型宣言を必要とするため、拡張または実装される型にはインクルードが必要です。

J2ObjC で生成されたヘッダー ファイルにインクルード サイクルを生成できます。このようなサイクルを取得するには、ファイル A のクラスを拡張するファイル B のクラスと、ファイル A のクラスを拡張するファイル B のクラスが必要です。これはありそうもないシナリオですが、Guava のコードベース(および他の場所)で発生します。

この問題を解決するには、.java ファイルで検出された Java 型ごとに個別のヘッダー ファイルを出力します。ただし、J2ObjC はビルドツールとして使用するように設計されており、優れたビルドシステムは入力ごとに予測可能な出力に依存しています。つまり、各 .java ファイルが 1 つの .h ファイルと 1 つの .m ファイルを生成する必要があります。

Foo.java:

class Foo extends Bar {}

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