Cabeçalhos segmentados

Os arquivos de cabeçalho gerados pelo J2ObjC são divididos em segmentos e podem ser incluídos um segmento de cada vez. Um segmento é criado para cada tipo de Java traduzido, de modo que cada classe interna terá seu próprio segmento. As macros de pré-processador são usadas para instruir o compilador a ler um segmento específico ao incluir o cabeçalho.Os cabeçalhos segmentados resolvem o problema de incluir ciclos nos cabeçalhos gerados pelo J2ObjC, descrito em detalhes abaixo.

Algumas informações importantes

  • Use #include em vez de #import para incluir cabeçalhos gerados pelo J2ObjC
    • O uso de #import com cabeçalhos segmentados é problemático porque o compilador pulará a leitura do cabeçalho se já o tiver visto. No entanto, como o cabeçalho é segmentado, ele pode não ter sido totalmente analisado pelo compilador na primeira vez.
  • Os cabeçalhos segmentados podem ser desativados com a sinalização --no-segmented-headers.

Circulares incluídos

Os arquivos principais gerados pelo J2ObjC precisam usar declarações inclusivas e de encaminhamento para resolver as informações de tipo necessárias. As declarações diretas são usadas o máximo possível, mas as inclusões são necessárias para tipos estendidos ou implementados porque o compilador exige a declaração de tipo completa.

É possível gerar ciclos de inclusão em arquivos de cabeçalho gerados pelo J2ObjC. Para criar esse ciclo, exigimos uma classe no arquivo A que estenda uma classe no arquivo B e uma classe no arquivo B que estenda uma classe no arquivo A. Esse é um cenário improvável, mas ocorre na base de código do Guava (e em outros lugares).

Uma correção natural para esse problema pode ser a emissão de um arquivo principal separado para cada tipo Java encontrado em um arquivo .java. No entanto, o J2ObjC foi projetado para ser usado como uma ferramenta de build, e qualquer bom sistema de build depende de saídas previsíveis para cada entrada. Isso significa que é necessário que cada arquivo .java produza exatamente um arquivo .h e um .m.

Exemplo

Foo.java:

class Foo extends Bar {}

Bar.java:

class Bar {
  static class Baz extends Foo {}
}

Foo.h (não segmentado):

#ifndef _Foo_H_
#define _Foo_H_

#include "Bar.h"
#include "J2ObjC_header.h"

@interface Foo : Bar
- (instancetype)init;
@end

#endif // _Foo_H_

Barra (não segmentado):

#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_

Observe que Foo.h inclui Bar.h e Bar.h inclui Foo.h. Como resultado, esses cabeçalhos não são compilados:

../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
~~~~~~~~~~~~~~   ^

Veja abaixo as versões segmentadas de Foo.h e Bar.h, que vão ser compiladas sem erros.

Foo.h (segmentado):

#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 (segmentado):

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