Pregunta @class vs. #import


A mi entender, uno debe usar una declaración de clase forward en caso de que ClassA necesite incluir un encabezado ClassB, y ClassB necesita incluir un encabezado ClassA para evitar inclusiones circulares. También entiendo que un #import es un simple ifndef para que una inclusión solo suceda una vez.

Mi pregunta es esta: ¿Cuándo se usa? #import y cuando usa uno @class? A veces si uso un @class declaración, veo una advertencia común del compilador como la siguiente:

warning: receiver 'FooController' is a forward class and corresponding @interface may not exist.

Realmente me encantaría entender esto, en lugar de simplemente eliminar el @class forward-declaration y lanzando un #import para silenciar las advertencias que el compilador me está dando.


696
2017-11-27 00:20


origen


Respuestas:


Si ve esta advertencia:

advertencia: el receptor 'MyCoolClass' es una clase directa y la interfaz @ correspondiente puede no existir

necesitas #import el archivo, pero puede hacerlo en su archivo de implementación (.m) y usar el @class declaración en su archivo de encabezado.

@class no (usualmente) elimina la necesidad de #import archivos, simplemente mueve el requisito más cerca de donde la información es útil.

Por ejemplo

Si usted dice @class MyCoolClass, el compilador sabe que puede ver algo como:

MyCoolClass *myObject;

No tiene que preocuparse por nada más que MyCoolClass es una clase válida, y debe reservar espacio para un puntero a ella (en realidad, solo un puntero). Por lo tanto, en tu encabezado, @class es suficiente el 90% del tiempo.

Sin embargo, si alguna vez necesita crear o acceder myObjectmiembros de s, tendrá que dejar que el compilador sepa cuáles son esos métodos. En este punto (presumiblemente en su archivo de implementación), necesitará #import "MyCoolClass.h", para decirle al compilador información adicional más allá de "esta es una clase".


735
2017-11-27 00:33



Tres reglas simples:

  • Solamente #import la superclase y los protocolos adoptados en los archivos de encabezado (.h archivos).
  • #import todas las clases y protocolos a los que envía mensajes en la implementación (.m archivos).
  • Declaraciones a futuro para todo lo demás.

Si reenvía la declaración en los archivos de implementación, entonces probablemente haga algo mal.


179
2017-08-29 00:34



Mire la documentación del lenguaje de programación Objective-C en ADC

En la sección sobre Definir una clase | Class Interface describe por qué se hace esto:

La directiva @class minimiza la cantidad de código visto por el compilador y el enlazador, y por lo tanto es la forma más simple de dar una declaración directa de un nombre de clase. Al ser simple, evita posibles problemas que pueden surgir al importar archivos que aún importan otros archivos. Por ejemplo, si una clase declara una variable de instancia tipada estáticamente de otra clase, y sus dos archivos de interfaz se importan entre sí, ninguna clase puede compilarse correctamente.

Espero que esto ayude.


110
2017-11-27 11:23



Utilice una declaración directa en el archivo de encabezado si es necesario, y #import los archivos de cabecera para cualquier clase que esté utilizando en la implementación. En otras palabras, siempre #import los archivos que está utilizando en su implementación, y si necesita hacer referencia a una clase en su archivo de encabezado, use también una declaración directa.

los excepción a esto es que deberías #import una clase o protocolo formal que está heredando en su archivo de encabezado (en cuyo caso no tendría que importarlo en la implementación).


47
2017-11-27 00:33



La práctica común es usar @class en los archivos de encabezado (pero aún necesita importar la superclase) y # import en los archivos de implementación. Esto evitará inclusiones circulares, y simplemente funciona.


24
2017-11-27 01:04



Otra ventaja: compilación rápida

Si incluye un archivo de encabezado, cualquier cambio en él hace que el archivo actual también se compile, pero este no es el caso si el nombre de la clase se incluye como @class name. Por supuesto, deberá incluir el encabezado en el archivo fuente


24
2017-09-09 08:02



Mi consulta es esto. ¿Cuándo se usa #import y cuándo se usa @class?

Respuesta simple: tu #import o #include cuando hay una dependencia física. De lo contrario, usa declaraciones directas (@class MONClass, struct MONStruct, @protocol MONProtocol)

Aquí hay algunos ejemplos comunes de dependencia física:

  • Cualquier valor C o C ++ (un puntero o referencia no es una dependencia física). Si tienes un CGPoint como un ivar o propiedad, el compilador deberá ver la declaración de CGPoint.
  • Tu superclase
  • Un método que usas

A veces, si utilizo una declaración de @clase, veo una advertencia de compilador común como la siguiente:      "advertencia: el receptor 'FooController' es una clase directa y la interfaz @ correspondiente puede no existir".

El compilador es muy indulgente a este respecto. Soltará sugerencias (como la de arriba), pero puede trash su pila fácilmente si las ignora y no lo hace #import correctamente. Aunque debería (IMO), el compilador no aplica esto. En ARC, el compilador es más estricto porque es responsable del recuento de referencias. Lo que sucede es que el compilador vuelve a un predeterminado cuando encuentra un método desconocido al que llama. Cada valor de retorno y parámetro se supone que es id. Por lo tanto, debe eliminar todas las advertencias de sus bases de código porque esto se debe considerar dependencia física. Esto es análogo a llamar a una función C que no está declarada. Con C, se supone que los parámetros son int.

La razón por la que preferiría enviar declaraciones es que puede reducir sus tiempos de compilación por factores porque hay una dependencia mínima. Con las declaraciones hacia adelante, el compilador ve que hay un nombre, y puede analizar y compilar correctamente el programa sin ver la declaración de clase o todas sus dependencias cuando no hay dependencia física. Las construcciones limpias toman menos tiempo. Las compilaciones incrementales toman menos tiempo. Seguro, terminará pasando un poco más de tiempo asegurándose de que todos los encabezados que necesita sean visibles para cada traducción como consecuencia, pero esto se compensa en tiempos de construcción reducidos rápidamente (suponiendo que su proyecto no sea pequeño).

Si utiliza #import o #include en su lugar, estás lanzando mucho más trabajo al compilador de lo necesario. También está presentando dependencias de encabezado complejas. Puedes comparar esto con un algoritmo de fuerza bruta. Cuando tú #import, está arrastrando toneladas de información innecesaria, que requiere mucha memoria, E / S de disco y CPU para analizar y compilar las fuentes.

ObjC es bastante cercano al ideal para un lenguaje basado en C con respecto a la dependencia porque NSObject los tipos nunca son valores - NSObject los tipos son siempre punteros contados de referencia. De modo que puede salirse con la suya en tiempos de compilación increíblemente rápidos si estructura las dependencias de su programa de forma apropiada y lo reenvía siempre que sea posible porque se requiere muy poca dependencia física. También puede declarar propiedades en las extensiones de clase para minimizar aún más la dependencia. Es una gran ventaja para los sistemas grandes: sabría la diferencia que hace si alguna vez ha desarrollado una gran base de código C ++.

Por lo tanto, mi recomendación es usar reenvíos siempre que sea posible, y luego #import donde hay dependencia física. Si ve la advertencia u otra que implica dependencia física, corríjalas todas. La solución es #import en tu archivo de implementación.

A medida que crea bibliotecas, probablemente clasifique algunas interfaces como un grupo, en cuyo caso lo haría #import esa biblioteca donde se introduce la dependencia física (p. #import <AppKit/AppKit.h>) Esto puede generar dependencia, pero los mantenedores de la biblioteca a menudo pueden manejar las dependencias físicas según sea necesario: si introducen una función, pueden minimizar el impacto que tiene en sus compilaciones.


18
2018-02-08 06:10



Veo mucho "Hazlo de esta manera", pero no veo ninguna respuesta a "¿Por qué?"

Asi que: Por qué ¿Deberías @clasificar en tu encabezado e importar solo en tu implementación? Doblas tu trabajo al tener que @class y Importar todo el tiempo. A menos que hagas uso de la herencia. En ese caso, tendrá #importing varias veces para una sola clase @. Luego debe recordar eliminar de múltiples archivos diferentes si de repente decide que ya no necesita acceder a una declaración.

Importar el mismo archivo varias veces no es un problema debido a la naturaleza de #import.  Compilar el rendimiento tampoco es realmente un problema. Si fuera así, no estaríamos importando # Cocoa / Cocoa.h o similar en casi todos los archivos de encabezado que tenemos.


11
2018-06-29 15:51