2010-07-29 13 views
7

Cuando compilo con el siguiente código no hay errores:¿Cuál es la diferencia entre @class y import

@class RootViewController; 
//#import "RootViewController.h" 

Cuando compilo con el siguiente código me sale un error:

//@class RootViewController; 
#import "RootViewController.h" 

"error: specifier-calificador-list esperado antes de 'RootViewController'"

No entiendo cuál es la diferencia entre los dos porque utilicé #import en una clase similar y se compiló sin errores!

+1

En su caso, hay algo mal en su archivo RootViewController.h, o uno de los archivos que importa. Tal vez un desaparecido ";" algun lado. Desgraciadamente, es un poco difícil depurar archivos de encabezado porque a menudo se obtiene el error en otro archivo. La razón por la que no obtiene el error cuando utiliza @class es que no carga el archivo que contiene el error. Ver mi respuesta para más detalles. – Felixyz

+0

posible duplicado de [Objective-C @class vs. #import] (http://stackoverflow.com/questions/322597/objective-c-class-vs-import) – willcodejavaforfood

Respuesta

8

decidí hacer referencia a la documentación porque todavía estaba confundido:

import

Esta directiva es idéntica a # include, excepto que se asegura de que el mismo archivo no se incluye nunca más de una vez. Por lo tanto, se prefiere y se usa en lugar de #include en ejemplos de código a lo largo de la documentación basada en Objective-C.

Esta convención significa que cada archivo de interfaz incluye, indirectamente, los archivos de interfaz para todas las clases heredadas. Cuando un módulo de origen importa una interfaz de clase, obtiene interfaces para toda la jerarquía de herencia sobre la que se basa la clase.

@class

Declaraciones de este tipo sólo tiene que utilizar el nombre de clase como un tipo y no dependen de los detalles de la interfaz de la clase (sus métodos y variables de instancia), la directiva @class da el compilador suficiente advertencia de qué esperar. Sin embargo, cuando se usa realmente la interfaz de una clase (instancias creadas, mensajes enviados), la interfaz de clase debe importarse.

http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-TPXREF123

+0

Por cierto, en caso de que se pregunte por qué no acepté una respuesta, es porque este sistema no me permite aceptar las respuestas hasta que pasaron 2 días, Dios sabe por qué es necesario. – TheLearner

6

@class se usa para evitar la dependencia circular ... Esto evita referencias circulares donde en un encabezado A importa un segundo encabezado B que (B) importa el primero (A) que importa el segundo (B) y así sucesivamente en un ciclo sin fin .... @class se utiliza generalmente para pedir compilador para buscar su definición en tiempo de ejecución ... especialmente cuando reside en alguna biblioteca estática ..

Aparte de eso #import funciona

See this question

+3

No estoy de acuerdo con que @class se use principalmente para evitar circular referencias, porque # import ya se ocupa de eso. (Pruébalo!) – Felixyz

+0

ok desacuerdo aceptado ... @class generalmente se usa para pedir al compilador que busque su definición en el tiempo de ejecución ... especialmente cuando reside en alguna biblioteca estática ... –

+1

@Felixyz: lo siento pero te equivocas . El uso principal de @class es evitar las deficiencias circulares de las definiciones @interface y @protocol. – JeremyP

0

@class significa que la definición de la clase RootViewController aún no se ha declarado, pero se definirá en tiempo de ejecución. Creo que es como declarar una clase externa en C++.

#import es equivalente a #include.

por el mensaje de error que podría adivinar que acaba de cometer un error en algún lugar dentro de RootViewController.h, como un olvidado; o algo así

+1

No es correcto decir que "se definirá en tiempo de ejecución". La clase definitivamente se definirá en algún momento durante la compilación. De hecho, ya podría estar definido. @class solo dice que en el contexto de este archivo, no vamos a acceder a ninguno de los campos o métodos de la clase, por lo que todo lo que necesitamos saber es su nombre para que podamos declararle los punteros. – Felixyz

6

Regla básica: use @class en su archivo de encabezado y #import en su archivo de implementación. (Sin embargo, es necesario #import superclase su clase. Y en algunas otras circunstancias también necesita usar `import" en el encabezado.)

#import no es equivalente a #include. Si un archivo es included muchas veces , se cargará cada vez, pero con muchos #imports del mismo archivo, solo se cargará una vez.

Por lo tanto, la razón principal para usar @class no es evitar las dependencias circulares, sino hacer que la compilación sea más rápida.

Aquí hay un ejemplo de cuándo debe usar @class

//MYControl.h 

@class MYControl; // Must use class 

@protocol MYControlDelegate 
-(void)control:(MYControl *)control didChangeToState:(UIControlState)state; 
@end 

@interface MYControl : UIControl 
{ 
    id<MYControlDelegate> delegate_; 
} 
@property (nonatomic, assign) id<MYControlDelegate> delegate; 
@end 

//MYControl.m 

@implementation MYControl 
@synthesize delegate = delegate_; 
. . . 

En este caso, no hay nada que importar, ya que el protocolo delegado se declara por encima de la clase principal en el archivo de cabecera. Pero aún necesita poder referirse a la clase principal que aún no ha sido declarada. Entonces, lo que hace @class es dejar que el compilador sepa que hay alguna clase que se llama MYControl y se definirá en algún momento. (No en tiempo de ejecución, sin embargo. La clase se definirá en el transcurso de la compilación.)

EDIT: Desde el manual de Objective-C:

Since declarations like this simply use the class name as a type and don’t depend on any details of the class interface (its methods and instance variables), the @class directive gives the compiler sufficient forewarning of what to expect. However, where the interface to a class is actually used (instances created, messages sent), the class interface must be imported. Typically, an interface file uses @class to declare classes, and the corresponding implementation file imports their interfaces (since it will need to create instances of those classes or send them messages).

The @class directive minimizes the amount of code seen by the compiler and linker, and is therefore the simplest way to give a forward declaration of a class name. Being simple, it avoids potential problems that may come with importing files that import still other files. For example, if one class declares a statically typed instance variable of another class, and their two interface files import each other, neither class may compile correctly.

Tenga en cuenta que la circularidad es mencionada en la última frase como uno en una clase general de los temas tratados mediante el uso de @class.

+0

¿Por qué dices @class no es para evitar las dependencias circulares y luego proporcionar un ejemplo en el que se usa precisamente de esa manera? En su ejemplo, la clase 'MyControl' tiene una dependencia en el protocolo' MyControlDelegate' y 'MyControlDelegate' tiene una dependencia en' MyControl'. – JeremyP

+0

Por cierto, sus declaraciones de variables y propiedades del tipo de delegado son incorrectas. Deberían ser así: 'id delegate_;' – JeremyP

+0

@JeremyP: sobre ivar/declaración de propiedad: gracias, lo he corregido. Sobre el problema de la circularidad, dije que las referencias circulares no son la razón * principal * para la existencia de @class. Más bien, el objetivo de @class es reducir las referencias de * any * para acelerar la compilación. Las referencias circulares también pueden manejarse de otras formas, aunque @class también es útil a este respecto. Creo que estas declaraciones se reflejan en la cita que agregué a mi respuesta. Habiendo señalado eso, nada me impide mostrar un ejemplo donde @class ayuda a manejar una referencia circular (dentro de un archivo). – Felixyz

25

@class se utiliza cuando se necesita conocer el nombre de una clase en un archivo en particular, pero usted no necesita saber nada de detalles acerca de la clase (sus métodos, por ejemplo). #import se usa cuando realmente necesita use la clase (es decir, envíele un mensaje).

Por ejemplo, si usted está declarando variables de instancia en un archivo de cabecera, puede utilizar @class para declarar una variable de instancia de un determinado tipo:

@class MyOtherClass; 

@interface MyClass : NSObject 
{ 
    MyOtherClass *myIvar; 
} 
@end 

Puesto que usted no está utilizando myIvar aún, no necesita saber nada al respecto, excepto que existe el tipo MyOtherClass.

Sin embargo:

#import "MyOtherClass.h" 

- (void)doSomething 
{ 
    [myIvar doSomethingElse]; 
} 

En este caso, usted está enviando el mensaje doSomethingElse-myIvar; el compilador necesita saber que las instancias de MyOtherClass definen este método, por lo que debe importar el archivo de encabezado o el compilador se quejará.

¿Por qué preocuparse por esto?

Principalmente tiene que ver con las dependencias. Cuando #import archiva A en el archivo B, el archivo B pasa a ser dependiente de en el archivo A, es decir, si el archivo A cambia, deberá volver a compilar el archivo B.Si usa @class en el archivo B, el archivo B no depende del archivo A, y por lo tanto no necesita ser recompilado cuando el archivo A cambia, por lo tanto, si solo está declarando un tipo y no depende de la implementación del archivo A, puede ahorrar tiempo de compilación no por #import ing archivo A

0

debe haber importado esta clase en la clase a la que desea importar aquí. Es por eso que está obteniendo un error, pero se puede rectificar mediante el ejemplo de @class.

0

@class es una declaración hacia adelante, una buena práctica es ponerlos en el .h en lugar de import para evitar el problema import circular.

Cuestiones relacionadas