J2ObjC 生成的头文件会分为多个部分,一次只能包含一个部分。系统会为每个转换的 Java 类型创建一个 segment,因此每个内部类都将拥有自己的 segment。预处理器宏用于指示编译器在包含头文件时仅读取特定部分。分段头文件可解决 J2ObjC 生成的头文件中包含循环的问题,本文档中对此进行了详细介绍。
须知事项
- 使用
#include
(而非#import
)包含 J2ObjC 生成的头文件。- 将
#import
与分段标头搭配使用会出现问题,因为如果编译器已经看到该标头,则会跳过读取该标头。但是,由于头文件是分段的,因此编译器可能在第一次运行时未完全解析该头文件。
- 将
- 您可以使用
--no-segmented-headers
标志停用分段标头。
圆形包含
J2ObjC 生成的头文件必须使用包含和前向声明来解析必要的类型信息。尽可能使用前向声明,但对于要扩展或实现的类型,必须使用包含,因为编译器需要完整的类型声明。
在 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")