Objective-C: ¿Directiva @class antes de @interface?

79

¿Cuál es la diferencia entre estas dos declaraciones de clase? No entiendo por qué se utiliza @class aquí. Gracias.

@class TestClass;

@interface TestClass: UIView {
    UIImage *image1;
    UIImage *image2;
}

y

@interface TestClass: UIView {
    UIImage *image1;
    UIImage *image2;
}
Ryan
fuente

Respuestas:

195

@classexiste para romper las dependencias circulares. Digamos que tienes clases A y B.

@interface A:NSObject
- (B*)calculateMyBNess;
@end

@interface B:NSObject
- (A*)calculateMyANess;
@end

Pollo; conoce a Egg. Esto nunca se puede compilar porque la interfaz de A depende de la definición de B y viceversa.

Por lo tanto, se puede solucionar usando @class:

@class B;
@interface A:NSObject
- (B*)calculateMyBNess;
@end

@interface B:NSObject
- (A*)calculateMyANess;
@end

@classle dice efectivamente al compilador que tal clase existe en algún lugar y, por lo tanto, los punteros declarados para apuntar a instancias de dicha clase son perfectamente válidos. Sin embargo, no podría llamar a un método en una referencia de instancia cuyo tipo solo se define como un @classporque no hay metadatos adicionales disponibles para el compilador (no recuerdo si revierte el sitio de la llamada para que se evalúe como una llamada a través de ido no).

En su ejemplo, el @classes inofensivo, pero completamente innecesario.

bbum
fuente
25
Ojalá pudiera dar otro +1 solo por 'pollo, encuentro huevo'
Swapnil Luktuke
4
Por cierto, vuelve a la idevaluación.
celebrado
¿Existen razones particulares por las que algunas personas @classingresan su archivo .h y luego importan los archivos de encabezado necesarios en su archivo .m, y otros pueden simplemente importar el archivo .h en el archivo de encabezado de clases actual? Solo curiosidad, gracias. @bbum
Josh Valdivieso
4
@JoshValdivieso Aún terminará rápidamente con dependencias circulares y el patrón general es minimizar el número de importaciones en el archivo de encabezado, ya que solía reducir significativamente los tiempos de compilación (ese no es tanto el caso en esta época de encabezados precompilados).
bbum
18
@class TestClass;

Esto simplemente declara "la clase TestClass será definida".

En este caso (el que pegaste), esto no tiene ningún efecto, por lo que son iguales.

Sin embargo, en caso de que vaya a definir un protocolo que use su nombre de clase (como tipo de parámetros pasados ​​al delegado, por ejemplo), deberá declarar @class TestClass antes de la definición del protocolo, ya que su clase aún no está definida.

En general, si necesita mencionar el nombre de su clase antes de realizar la definición de la clase, @classprimero deberá emitir una declaración

poncha
fuente
He visto más esta implementación antes que los protocolos. ¡gracias por compartir!
SleepsOnNewspapers
7

Según la respuesta de Matt, no tiene ningún sentido la @classdeclaración en su código.@classforward define una clase para que el compilador sepa posteriormente a qué tipo general de unidad se refiere. Dado que Objective-C no tiene prácticamente ningún tipo en tiempo de ejecución, a menudo eso es todo lo que el compilador necesita saber, lo suficiente para distinguir la cosa de un valor C atómico.

Voy a intentarlo en la oscuridad y diré que debido a que las variables de instancia están declaradas en el @interface, estás viendo un código antiguo. Debido a que es un código antiguo, @classprobablemente solía estar en otro lugar (por ejemplo, había un protocolo de delegado declarado en el medio) y acaba de terminar inofensivamente varado.

Tommy
fuente
4

@class es bastante útil cuando necesita definir un protocolo para un objeto que normalmente interactuará con el objeto cuya interfaz también está definiendo. Usando @class, puede mantener la definición del protocolo en el encabezado de su clase. Este patrón de delegación se usa a menudo en Objective-C y, a menudo, es preferible a definir tanto "MyClass.h" como "MyClassDelegate.h". Eso puede causar algunos problemas de importación confusos.

@class MyClass;

@protocol MyClassDelegate<NSObject>

- (void)myClassDidSomething:(MyClass *)myClass
- (void)myClass:(MyClass *)myClass didSomethingWithResponse:(NSObject *)reponse
- (BOOL)shouldMyClassDoSomething:(MyClass *)myClass;
- (BOOL)shouldMyClass:(MyClass *)myClass doSomethingWithInput:(NSObject *)input

@end

// MyClass hasn't been defined yet, but MyClassDelegate will still compile even tho
// params mention MyClass, because of the @class declaration.
// You're telling the compiler "it's coming. don't worry".
// You can't send MyClass any messages (you can't send messages in a protocol declaration anyway),
// but it's important to note that @class only lets you reference the yet-to-be-defined class. That's all.
// The compiler doesn't know anything about MyClass other than its definition is coming eventually.

@interface MyClass : NSObject

@property (nonatomic, assign) id<MyClassDelegate> delegate;

- (void)doSomething;
- (void)doSomethingWithInput:(NSObject *)input

@end

Luego, cuando esté usando la clase, puede crear instancias de la clase e implementar el protocolo con una sola declaración de importación

#import "MyClass.h"

@interface MyOtherClass()<MyClassDelegate>

@property (nonatomic, strong) MyClass *myClass;

@end

@implementation MyOtherClass

#pragma mark - MyClassDelegate Protocol Methods

- (void)myClassDidSomething:(MyClass *)myClass {

    NSLog(@"My Class Did Something!")

}

- (void)myClassDidSomethingWithResponse:(NSObject *)response {

    NSLog(@"My Class Did Something With %@", response);

}

- (BOOL)shouldMyClassDoSomething {

    return YES;

- (BOOL)shouldMyClassDoSomethingWithInput:(NSObject *)input {

    if ([input isEqual:@YES]) {

        return YES;

    }

    return NO;

}


- (void)doSomething {

    self.myClass = [[MyClass alloc] init];
    self.myClass.delegate = self;
    [self.myClass doSomething];
    [self.myClass doSomethingWithInput:@0];

}
vsanthanam510
fuente