ヘッダーの分割

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

知っておくべきポイント

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

円形インクルード

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

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

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

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