區隔式標題

J2ObjC 產生的標頭檔案分為多個片段,您一次只能加入一個片段。系統會為每個已轉譯的 Java 類型建立一個區隔,因此每個內部類別都會有自己的區隔。預先處理工具巨集可以指示編譯器在加入 header.Segmented 標頭時,只能讀取特定片段,解決 J2ObjC 產生的標頭中納入循環的問題,如下所述。

銷售須知

  • 如要加入 J2ObjC 產生的標頭,請改用 #include,而非 #import
    • 搭配區隔標頭使用 #import 會發生問題,因為如果已看過標頭,編譯器會略過讀取作業。不過,由於標頭是分段的,編譯器可能第一次未完整剖析標頭。
  • 您可以使用 --no-segmented-headers 旗標停用區隔標頭。

圓形內含

J2ObjC 產生的標頭檔案必須使用 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 包含 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")