framework español objective-c cocoa cocoa-touch

objective c - español - @class vs.#import



cocoa touch español (16)

Mi pregunta es la siguiente. ¿Cuándo se usa #import y cuándo se usa @class?

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

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

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

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

El compilador en realidad es muy indulgente en este sentido. Soltará sugerencias (como la de arriba), pero puedes desechar la pila fácilmente si las ignoras y no #import correctamente. Aunque debería (IMO), el compilador no hace cumplir esto. En ARC, el compilador es más estricto porque es responsable del conteo de referencias. Lo que sucede es que el compilador vuelve a su valor predeterminado cuando encuentra un método desconocido al que llama. Se supone que cada valor y parámetro de retorno es id . Por lo tanto, deberías erradicar todas las advertencias de tus bases de código porque esto debería considerarse dependencia física. Esto es análogo a llamar a una función C que no está declarada. Con C, se asume que los parámetros son int .

La razón por la que favorecería las declaraciones a futuro es que puede reducir los tiempos de construcción por factores porque existe una dependencia mínima. Con las declaraciones de reenvío, 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 llevan menos tiempo. Las construcciones incrementales toman menos tiempo. Claro, usted terminará gastando 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 reduce rápidamente en los tiempos de compilación reducidos (suponiendo que su proyecto no sea muy pequeño).

Si usa #import o #include lugar, está lanzando mucho más trabajo al compilador del que es necesario. También estás introduciendo dependencias de encabezado complejas. Puedes comparar esto con un algoritmo de fuerza bruta. Cuando realiza la #import , arrastra toneladas de información innecesaria, que requiere mucha memoria, E / S de disco y CPU para analizar y compilar las fuentes.

ObjC es casi ideal para un lenguaje basado en C con respecto a la dependencia porque los tipos NSObject nunca son valores, los tipos NSObject son siempre punteros contados de referencia. Por lo tanto, puede salirse con tiempos de compilación increíblemente rápidos si estructura las dependencias de su programa de manera adecuada y adelante cuando sea posible, ya que 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 los sistemas grandes: sabría la diferencia que hace si alguna vez ha desarrollado una base de código de C ++ grande.

Por lo tanto, mi recomendación es usar los forwards cuando sea posible, y luego #import donde haya dependencia física. Si ve la advertencia u otra que implique dependencia física, corríjalos a todos. La solución es #import en su archivo de implementación.

A medida que construye bibliotecas, es probable que clasifique algunas interfaces como un grupo, en cuyo caso #import <AppKit/AppKit.h> esa biblioteca donde se introduce la dependencia física (por ejemplo, #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 característica, pueden minimizar el impacto que tiene en sus construcciones.

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

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

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

Me encantaría entender esto, en lugar de simplemente quitar la @class hacia adelante @class y lanzar un #import para silenciar las advertencias que me da el compilador.


Cuando me desarrollo, solo tengo tres cosas en mente que nunca me causan ningún problema.

  1. Importar súper clases
  2. Importar clases para padres (cuando tengas hijos y padres)
  3. Importar clases fuera de su proyecto (como en marcos y bibliotecas)

Para todas las demás clases (subclases y clases secundarias en mi propio proyecto), las declaro mediante clase avanzada.


El compilador se quejará solo si va a utilizar esa clase de tal manera que el compilador necesita saber su implementación.

Ex:

  1. Esto podría ser como si vas a derivar tu clase de esto o
  2. Si va a tener un objeto de esa clase como una variable miembro (aunque rara).

No se quejará si solo lo va a utilizar como puntero. Por supuesto, deberá # importarlo en el archivo de implementación (si está creando una instancia de un objeto de esa clase) ya que necesita conocer el contenido de la clase para instanciar un objeto.

NOTA: #import no es lo mismo que #include. Esto significa que no hay nada llamado importación circular. importación es una especie de solicitud para que el compilador examine un archivo en particular para obtener información. Si esa información ya está disponible, el compilador la ignora.

Solo intente esto, importe Ah en Bh y Bh en Ah. No habrá problemas ni quejas y también funcionará bien.

Cuándo usar @class

Utiliza @class solo si ni siquiera quieres importar un encabezado en tu encabezado. Este podría ser un caso en el que ni siquiera te importa saber cuál será esa clase. Casos en los que puede que ni siquiera tengas un encabezado para esa clase todavía.

Un ejemplo de esto podría ser que estás escribiendo dos bibliotecas. Una clase, llamémosla A, existe en una biblioteca. Esta biblioteca incluye un encabezado de la segunda biblioteca. Ese encabezado puede tener un puntero de A, pero de nuevo puede no necesitar usarlo. Si la biblioteca 1 aún no está disponible, la biblioteca B no se bloqueará si utiliza @class. Pero si está buscando importar Ah, entonces el progreso de la biblioteca 2 está bloqueado.


Este es un ejemplo de escenario, donde necesitamos @class.

Considere si desea crear un protocolo dentro del archivo de cabecera, 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.

// DroneSearchField.h #import <UIKit/UIKit.h> @class DroneSearchField; @protocol DroneSearchFieldDelegate<UITextFieldDelegate> @optional - (void)DroneTextFieldButtonClicked:(DroneSearchField *)textField; @end @interface DroneSearchField : UITextField @end


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


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

Bajo la sección de Definir una Clase | Interfaz de clase describe por qué se hace esto:

La directiva @class minimiza la cantidad de código que ven el compilador y el vinculador, y por lo tanto, es la forma más sencilla de dar una declaración de un nombre de clase. Siendo simple, evita problemas potenciales que pueden venir con la importación de archivos que importan aún otros archivos. Por ejemplo, si una clase declara una variable de instancia tipificada estáticamente de otra clase, y sus dos archivos de interfaz se importan entre sí, ninguna clase puede compilar correctamente.

Espero que esto ayude.


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.


Piense en @class como diciendo al compilador "confía en mí, esto existe".

Piense en #import como copiar y pegar.

Desea minimizar el número 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.

Observe que cuando hereda de una clase, no puede simplemente usar una declaración de reenvío. Debe importar el archivo para que la clase que está declarando sepa cómo está definido.


Reenvíe la declaración solo al compilador de prevención para que no muestre error.

el compilador sabrá que hay una clase con el nombre que ha usado en su archivo de encabezado para declarar.


Si intenta declarar una variable, o una propiedad en su archivo de encabezado, que aún no ha importado, obtendrá un error que indica que el compilador no conoce esta clase.

Tu primer pensamiento es probablemente #import .
Esto puede causar problemas en algunos casos.

Por ejemplo, si implementas 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 puedes decirle al compilador con @class :

Sé que no sabes esa clase, pero existe. Se va a importar o implementar en otro lugar

Básicamente, le dice al compilador que se calme y compile, aunque no esté seguro si alguna vez se implementará esta clase.

Usualmente usará #import en .m y @class en los archivos .h .


Si ve esta advertencia:

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

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

@class no (por lo general) elimina 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:

MyCoolClass *myObject;

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

Sin embargo, si alguna vez necesita crear o acceder a los miembros de myObject , deberá informar al compilador de 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".


Tres reglas simples:

  • Solo #import la #import , y protocolos adoptados, en archivos de cabecera (archivos .h ).
  • #import todas las clases y protocolos, envía mensajes a en la implementación (archivos .m ).
  • Reenviar declaraciones para todo lo demás.

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


Use 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é usando en la implementación. En otras palabras, siempre #import importa los archivos que está usando en su implementación, y si necesita hacer referencia a una clase en su archivo de encabezado, use también una declaración de reenvío.

La excepción a esto es que debe #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).


Veo un montón de "Hazlo de esta manera" pero no veo ninguna respuesta a "¿Por qué?"

Entonces: ¿ Por qué debería @class en su encabezado y #importar solo en su implementación? Estás duplicando tu trabajo teniendo que @class y #import todo el tiempo. A menos que hagas uso de la herencia. En cuyo caso, estarás #importando 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.


para obtener información adicional sobre las dependencias de archivos & #import & @class, compruebe esto:

http://qualitycoding.org/file-dependencies/ itis buen artículo

resumen del artículo

Importaciones en archivos de cabecera:

  • #Importe la superclase que está heredando y los protocolos que está implementando.
  • Reenviar-declarar todo lo demás (a menos que provenga de un marco con un encabezado maestro).
  • Intenta eliminar todos los demás #importes.
  • Declaran protocolos en sus propios encabezados para reducir dependencias.
  • ¿Demasiadas declaraciones a futuro? Tienes una clase grande.

Importaciones en archivos de implementación:

  • Elimina los #portes de cruceros que no se utilizan.
  • Si un método delega a otro objeto y devuelve lo que recibe, intente declarar hacia adelante ese objeto en lugar de # importarlo.
  • Si incluir un módulo lo obliga a incluir nivel tras nivel de dependencias sucesivas, es posible que tenga un conjunto de clases que desee convertirse en una biblioteca. Constrúyalo como una biblioteca separada con un encabezado maestro, de modo que todo se pueda incluir como un solo fragmento creado previamente.
  • Demasiados #importaciones? Tienes una clase grande.

si hacemos esto

@interface Class_B : Class_A

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

#import .... @class Class_A @interface Class_B

Aquí estamos diciendo que estamos usando la 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 su función y variables).