细分标头

J2ObjC 生成的头文件会分为若干段,一次可以包含一个段。系统会为每个转换后的 Java 类型创建一个段,因此每个内部类都将有自己的段。预处理器宏用于告知编译器在包含标头时仅读取特定片段。分段标头解决了 J2ObjC 生成的标头中的包含循环问题,详见下文。

须知事项

  • 请使用 #include(而非 #import)包含 J2ObjC 生成的头文件。
    • #import 与分段标头一起使用会带来问题,因为编译器会跳过已看到标头的读取步骤。但是,由于标头已细分,因此编译器在第一次时可能未完全解析该标头。
  • 您可以使用 --no-segmented-headers 标志停用分段标头。

循环包含

J2ObjC 生成的头文件必须使用 include 和前向声明来解析必要的类型信息。尽可能多地使用前向声明,但对于要扩展或实现的类型,必须使用 include,因为编译器需要完整的类型声明。

可以在 J2ObjC 生成的头文件中生成包含循环。为了实现这种循环,我们需要文件 A 中的一个类扩展文件 B 中的一个类,以及文件 B 中的一个类扩展文件 A 中的类。这种情况不太可能发生,但确实发生在 Guava 的代码库(及其他位置)中。

此问题的自然解决方法可能是为 .java 文件中遇到的每种 Java 类型发出单独的头文件。但 J2ObjC 旨在用作构建工具,任何良好的构建系统都依赖于每个输入的可预测输出。也就是说,每个 .java 文件必须正好生成一个 .h 文件和一个 .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")