Tengo entendido que uno debe usar una declaración de clase directa en el caso de que ClassA necesite incluir un encabezado de ClassB, y ClassB debe incluir un encabezado de ClassA para evitar inclusiones circulares. También entiendo que an #import
es simple, ifndef
por lo que una inclusión solo ocurre una vez.
Mi pregunta es esta: ¿cuándo se usa #import
y cuándo se usa @class
? A veces, si uso una @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 la @class
declaración de avance y lanzar un #import
silencio para silenciar las advertencias que me está dando el compilador.
fuente
Respuestas:
Si ves esta advertencia:
necesita
#import
el archivo, pero puede hacerlo en su archivo de implementación (.m) y usar la@class
declaración en su archivo de encabezado.@class
no elimina (por lo general) la necesidad de#import
archivos, simplemente mueve el requisito más cerca de donde la información es útil.Por ejemplo
Si dices
@class MyCoolClass
, el compilador sabe que puede ver algo como:No tiene que preocuparse por otra cosa que no
MyCoolClass
sea una clase válida, y debe reservar espacio para un puntero (en realidad, solo un puntero). Por lo tanto, en su encabezado, es@class
suficiente el 90% del tiempo.Sin embargo, si alguna vez necesita crear o acceder a
myObject
los miembros, deberá informar al compilador cuáles son esos métodos. En este punto (presumiblemente en su archivo de implementación), tendrá#import "MyCoolClass.h"
que decirle al compilador información adicional más allá de "esta es una clase".fuente
@class
algo en su.h
archivo, pero se olvidan de#import
que en el .m, tratar de acceder a un método en el@class
objeto ed, y obtener advertencias como:warning: no -X method found
.Tres reglas simples:
#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).Si reenvía la declaración en los archivos de implementación, entonces probablemente haga algo mal.
fuente
Mire la documentación del lenguaje de programación Objective-C en ADC
En la sección sobre Definición de una clase | La interfaz de clase describe por qué se hace esto:
Espero que esto ayude.
fuente
Utilice una declaración de reenvío en el archivo de encabezado si es necesario, y
#import
los archivos de encabezado para cualquier clase que esté utilizando en la implementación. En otras palabras, siempre#import
utiliza los archivos que está utilizando en su implementación, y si necesita hacer referencia a una clase en su archivo de encabezado, utilice también una declaración directa.La excepción a esto es que debería tener
#import
una clase o protocolo formal del que está heredando en su archivo de encabezado (en cuyo caso no necesitaría importarlo en la implementación).fuente
La práctica común es usar @class en archivos de encabezado (pero aún necesita # importar la superclase) e # importar en archivos de implementación. Esto evitará cualquier inclusión circular, y simplemente funciona.
fuente
#import
"es como la directiva #include de C, excepto que se asegura de que el mismo archivo nunca se incluya más de una vez". Entonces, según esto#import
se ocupa de las inclusiones circulares, las@class
directivas no ayudan particularmente con eso.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 fuentefuente
Respuesta simple: usted
#import
o#include
cuando hay una dependencia física. De lo contrario, se utiliza declaraciones adelantadas (@class MONClass
,struct MONStruct
,@protocol MONProtocol
).Aquí hay algunos ejemplos comunes de dependencia física:
CGPoint
como ivar o propiedad, el compilador deberá ver la declaración deCGPoint
.El compilador es realmente muy indulgente a este respecto. Caerá pistas (como la anterior), pero puede tirar basura a la 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 recurre a un valor predeterminado cuando encuentra un método desconocido al que llama. Se supone que cada valor de retorno y parámetro esid
. Por lo tanto, debe erradicar todas las advertencias de sus bases de código porque esto debe considerarse dependencia física. Esto es análogo a llamar a una función C que no se declara. Con C, se supone que los parámetros sonint
.La razón por la que preferiría las declaraciones adelantadas es que puede reducir sus tiempos de compilación por factores porque hay una dependencia mínima. Con las declaraciones directas, 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 construcciones incrementales toman menos tiempo. Claro, 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 amortiza en tiempos de construcción reducidos rápidamente (suponiendo que su proyecto no sea pequeño).
Si usa
#import
o#include
, en cambio, está lanzando mucho más trabajo en el compilador de lo necesario. También está introduciendo dependencias complejas de encabezado. Puede comparar esto con un algoritmo de fuerza bruta. Cuando usted#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 ideal para un lenguaje basado en C con respecto a la dependencia porque los
NSObject
tipos nunca son valores, losNSObject
tipos son siempre punteros contados por referencia. Por lo tanto, puede escapar con tiempos de compilación increíblemente rápidos si estructura las dependencias de su programa de manera adecuada y reenvía donde 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. Esa es una gran ventaja para 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 a
#import
donde haya dependencia física. Si ve la advertencia u otra que implique dependencia física, corríjalas todas. La solución está#import
en su archivo de implementación.A medida que construye bibliotecas, es probable que clasifique algunas interfaces como un grupo, en cuyo caso elegiría
#import
la biblioteca donde se introduce la dependencia física (por ejemplo#import <AppKit/AppKit.h>
). Esto puede introducir dependencia, pero los mantenedores de la biblioteca a menudo pueden manejar las dependencias físicas por usted según sea necesario; si presentan una característica, pueden minimizar el impacto que tiene en sus compilaciones.fuente
NSObject types are never values -- NSObject types are always reference counted pointers.
No del todo cierto. Los bloques arrojan un vacío en su respuesta, solo diciendo.Veo mucho "Hazlo de esta manera" pero no veo ninguna respuesta a "¿Por qué?"
Entonces: ¿ Por qué debería @class en su encabezado e #importar solo en su implementación? Estás duplicando tu trabajo al tener que @clase y #importar todo el tiempo. A menos que hagas uso de la herencia. En cuyo caso, #importará varias veces para una sola @clase. Luego debe recordar eliminar de varios 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 lo fuera, no estaríamos #importando Cocoa / Cocoa.h o similares en casi todos los archivos de encabezado que tenemos.
fuente
si hacemos esto
significa que estamos heredando Class_A en Class_B, en Class_B podemos acceder a todas las variables de class_A.
si estamos haciendo esto
aquí decimos que estamos usando Class_A en nuestro programa, pero si queremos usar las variables Class_A en Class_B tenemos que # importar Class_A en el archivo .m (hacer un objeto y usar sus funciones y variables).
fuente
para obtener información adicional sobre las dependencias de archivos y #importación y @clase, consulte esto:
http://qualitycoding.org/file-dependencies/ es un buen artículo
resumen del artículo
fuente
Cuando me desarrollo, solo tengo tres cosas en mente que nunca me causan ningún problema.
Para todas las demás clases (subclases y clases secundarias en mi propio proyecto), las declaro a través de la clase forward.
fuente
Si intenta declarar una variable o una propiedad en su archivo de encabezado, que aún no importó, obtendrá un error que indica que el compilador no conoce esta clase.
Tu primer pensamiento es probablemente
#import
eso.Esto puede causar problemas en algunos casos.
Por ejemplo, si implementa un montón de métodos C en el archivo de encabezado, o estructuras, o algo similar, porque no deberían importarse varias veces.
Por lo tanto, puede decirle al compilador con
@class
:Básicamente le dice al compilador que se calle y compile, aunque no está seguro si esta clase se implementará alguna vez.
Se suele utilizar
#import
en el .m y@class
en los .h archivos.fuente
Reenvíe la declaración solo para evitar que el compilador muestre un error.
el compilador sabrá que hay una clase con el nombre que ha utilizado en su archivo de encabezado para declarar.
fuente
El compilador se quejará solo si va a usar esa clase de tal manera que el compilador necesite saber su implementación.
Ex:
No se quejará si solo lo va a usar como puntero. Por supuesto, tendrá que # importarlo en el archivo de implementación (si está instanciando un objeto de esa clase) ya que necesita conocer el contenido de la clase para crear una instancia de un objeto.
NOTA: #import no es lo mismo que #include. Esto significa que no hay nada llamado importación circular. importar es una especie de solicitud para que el compilador busque información en un archivo en particular. Si esa información ya está disponible, el compilador la ignora.
Simplemente intente esto, importe Ah en Bh y Bh en Ah No habrá problemas ni quejas y también funcionará bien.
Cuando usar @class
Utiliza @class solo si ni siquiera desea importar un encabezado en su encabezado. Este podría ser un caso en el que ni siquiera te importa saber cuál será esa clase. Casos en los que es posible que ni siquiera tenga un encabezado para esa clase todavía.
Un ejemplo de esto podría ser que está escribiendo dos bibliotecas. Una clase, llamémosla A, existe en una biblioteca. Esta biblioteca incluye un encabezado de la segunda biblioteca. Ese encabezado podría tener un puntero de A, pero nuevamente podría no necesitar usarlo. Si la biblioteca 1 aún no está disponible, la biblioteca B no se bloqueará si usa @class. Pero si está buscando importar Ah, entonces el progreso de la biblioteca 2 está bloqueado.
fuente
Piense en @class como diciéndole al compilador "confía en mí, esto existe".
Piense en #import como copiar y pegar.
Desea minimizar la cantidad de importaciones que tiene por varias razones. Sin ninguna investigación, lo primero que viene a la mente es que reduce el tiempo de compilación.
Tenga en cuenta que cuando hereda de una clase, no puede simplemente usar una declaración directa. Debe importar el archivo para que la clase que declara sepa cómo se define.
fuente
Este es un escenario de ejemplo, donde necesitamos @class.
Considere si desea crear un protocolo dentro del archivo de encabezado, que tiene un parámetro con el tipo de datos de la misma clase, entonces puede usar @class. Recuerde que también puede declarar protocolos por separado, esto es solo un ejemplo.
fuente