區隔式標題

J2ObjC 產生的標頭檔案會分成多個區段,且每次只能納入一個區段。系統會為每個已翻譯的 Java 類型建立一個區段,因此每個內部類別都會有自己的區段。預處理器巨集可用於告訴編譯器,在納入標頭時只讀取特定區段。分割式標頭可解決 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")