Encabezados segmentados

Los archivos de encabezados generados por J2ObjC se dividen en segmentos y se pueden incluir un segmento a la vez. Se crea un segmento para cada tipo de Java traducido, de modo que cada clase interna tendrá su propio segmento. Las macros del preprocesador se usan para indicar al compilador que solo debe leer un segmento en particular cuando se incluye el encabezado.Los encabezados segmentados resuelven el problema de los ciclos de inclusión en los encabezados generados por J2ObjC, que se describen en detalle a continuación.

Qué debe saber

  • Usa #include en lugar de #import para incluir los encabezados generados por J2ObjC.
    • El uso de #import con encabezados segmentados resulta problemático porque el compilador omitirá la lectura del encabezado si ya lo vio. Sin embargo, debido a que el encabezado está segmentado, es posible que el compilador no lo haya analizado por completo la primera vez.
  • Los encabezados segmentados se pueden inhabilitar con la marca --no-segmented-headers.

Círculos incluidos

Los archivos de encabezado generados por J2ObjC deben usar declaraciones de inclusión y reenvío para resolver la información de tipo necesaria. Las declaraciones directas se usan tanto como sea posible. Sin embargo, las inclusiones son necesarias para los tipos que se extienden o implementan, ya que el compilador requiere la declaración completa del tipo.

Es posible generar ciclos de inclusión en archivos de encabezados generados por J2ObjC. Para obtener ese ciclo, necesitamos una clase en el archivo A que extienda una clase en el archivo B y una clase en el archivo B que extienda una clase en el archivo A. Esta situación es poco probable, pero ocurre en la base de código de Guava (y en otros lugares).

Una solución natural para este problema podría ser emitir un archivo de encabezado separado para cada tipo de Java encontrado en un archivo .java. Sin embargo, J2ObjC está diseñado para usarse como una herramienta de compilación, y cualquier buen sistema de compilación se basa en resultados predecibles para cada entrada. Eso significa que es necesario que cada archivo .java produzca exactamente un archivo .h y uno .m.

Ejemplo

Foo.java:

class Foo extends Bar {}

Bar.java:

class Bar {
  static class Baz extends Foo {}
}

Foo.h (sin segmento):

#ifndef _Foo_H_
#define _Foo_H_

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

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

#endif // _Foo_H_

Bar.h (no 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_

Ten en cuenta que Foo.h incluye Bar.h y Bar.h incluye Foo.h. Por lo tanto, no se pudieron compilar estos encabezados:

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

A continuación, se muestran las versiones segmentadas de Foo.h y Bar.h que se compilarán sin errores.

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