objective-c - tutorial - objective c ultima version
AnulaciĆ³n de declaraciones de propiedad en Objective-C (2)
A menudo encuentro que sé que cierta propiedad de una clase base siempre será un tipo determinado en una subclase. Por ejemplo, en el ejemplo a continuación, la propiedad obj
siempre será un objeto NSString en Derivado. Sin embargo, necesito que esta propiedad sea el tipo de identificación más genérico en la clase Base.
@interface Base
@property (strong, nonatomic) id obj;
@end
@implementation Base
//@synthesize obj = obj_;
@dynamic obj;
@end
@interface Derived : Base
@property (strong, nonatomic) NSString *obj;
@end
@implementation Derived
@synthesize obj = obj_;
@end
¿Este código es correcto? Me preocupa que @synthesize aparezca dos veces. ¿Está esto creando dos propiedades, o la declaración @synthesize en Derivado anula la de Base?
Edición: Cambiar @synthesize a @dynamic en Base tiene más sentido.
Editar: Esto requiere iOS SDK 5.
Las subclases pueden cambiar los tipos asociados con los métodos. En general, una subclase puede especializar un tipo de retorno y puede hacer que los tipos de argumento sean más genéricos. En realidad, hay un nombre para esto, pero no puedo recordar cuál es. De todos modos, aquí está lo racional:
Tipos de retorno
Si tengo una clase
@interface A
- (id)foo;
@end
y otra clase
@interface B : A
- (NSString *)foo;
@end
Y tengo una instancia B* b
, puedo convertirla en A*
y aún cumplir con la firma de tipo del método -[A foo]
, porque cualquier NSString*
también es una id
.
Sin embargo, no puedo hacer esto más generalizado. Si en cambio tengo
@interface A
- (NSString *)foo;
@end
@interface B : A
- (id)foo;
@end
Y tengo una instancia B* b
y la NSString *
a A*
, entonces el tipo de [(A*)b foo]
es NSString *
y, sin embargo, el valor real puede ser cualquier id
, porque ese es el tipo que declaré -[B foo]
ser. Esto es una violación del sistema de tipos.
Argumentos
Si tengo una clase
@interface A
- (void)foo:(NSString *)obj;
@end
y otra clase
@interface B : A
- (void)foo:(id)obj;
@end
Y tengo una instancia B* b
y la reduzco a A*
, entonces cualquier argumento válido para [(A*)b foo:obj]
también se ajusta al tipo de -[B foo:]
, porque cualquier NSString *
es También una id
.
Sin embargo si tengo los siguientes
@interface A
- (void)foo:(id)obj;
@end
@interface B : A
- (void)foo:(NSString *)obj;
@end
Y tengo una instancia B* b
y la transfiero a A*
, luego puedo pasar cualquier id
a [(A*)b foo:obj]
, pero la clase B
subyacente solo espera NSString*
s. Y así he violado el sistema de tipos.
Propiedades
Aquí está el punto pegajoso. Cuando declara el tipo de una propiedad, declara tanto el tipo de retorno del captador como el tipo de argumento del establecedor. De acuerdo con las reglas anteriores, esto significa que no puede cambiar el tipo de una propiedad, porque en uno de los dos casos violará el sistema de tipos.
Lo anterior es la teoría. En la práctica, no tengo idea si GCC o Clang imponen estas restricciones. Es posible que asuman que el programador sabe mejor, y que generalizar o especializar incorrectamente un tipo romperá silenciosamente el sistema de tipos detrás de su espalda. Tendrás que experimentar. Pero si el compilador es realmente correcto, entonces no se permitirá generalizar los tipos de retorno y los argumentos especializados. Y eso significa que no se permitirá cambiar el tipo de propiedad.
Incluso si el compilador lo permite, probablemente no deberías hacerlo. Romper silenciosamente el sistema de tipos es una excelente manera de introducir errores y un indicador de arquitectura deficiente.
Realmente no puedes hacer eso. Para mi recibo un error
property ''obj'' attempting to use ivar ''obj_'' declared in super class of ''Derived''
Así que creo que es obvio. E incluso si no usa @synthesize y define funciones usted mismo, entonces solo se llama a la versión Derivada (no existe tal cosa como la sobrecarga de funciones o funciones virtuales en ObjC).
Tienes que revisar el diseño de tu clase.