objective-c - studio - superponer graficas en r
¿Propiedades de solo lectura en Objective-C? (7)
He declarado una propiedad de solo lectura en mi interfaz como tal:
@property (readonly, nonatomic, copy) NSString* eventDomain;
Tal vez estoy malinterpretando las propiedades, pero pensé que cuando lo declaraste de manera que fuera de readonly
, puedes usar el setter generado dentro del archivo de implementación ( .m
), pero las entidades externas no pueden cambiar el valor. Esta pregunta SO dice que eso es lo que debería suceder. Ese es el comportamiento que busco. Sin embargo, cuando trato de usar el setter estándar o la sintaxis de punto para establecer eventDomain
dentro de mi método init, me da un unrecognized selector sent to instance.
error. Por supuesto que estoy @synthesize
la propiedad. Tratando de usarlo así:
// inside one of my init methods
[self setEventDomain:@"someString"]; // unrecognized selector sent to instance error
Entonces, ¿estoy malinterpretando la declaración de readonly
sobre una propiedad? ¿O algo más está pasando?
Consulte Personalización de clases existentes en los documentos de iOS.
readonly Indica que la propiedad es de solo lectura. Si especifica de solo lectura, solo se requiere un método getter en @implementation. Si usa @synthesize en el bloque de implementación, solo se sintetizará el método getter. Además, si intentas asignar un valor usando la sintaxis de punto, obtienes un error de compilación.
Las propiedades de solo lectura solo tienen un método getter. Todavía puede establecer el ivar de respaldo directamente dentro de la clase de la propiedad o usando la codificación del valor clave.
Eiko y otros dieron respuestas correctas.
Aquí hay una manera más simple: acceder directamente a la variable de miembro privado.
Ejemplo
En el archivo .h de encabezado:
@property (strong, nonatomic, readonly) NSString* foo;
En el archivo .m de implementación:
// inside one of my init methods
self->_foo = @"someString"; // Notice the underscore prefix of var name.
Eso es todo, eso es todo lo que necesitas. Sin despeinarse sin problemas.
Detalles
A partir de Xcode 4.4 y LLVM Compiler 4.0 ( Nuevas funciones en Xcode 4.4 ), no necesita meterse con los quehaceres discutidos en las otras respuestas:
- La palabra clave
synthesize
- Declarando una variable
- Re-declarar la propiedad en el archivo .m de implementación.
Después de declarar un foo
propiedad, puede suponer que Xcode ha agregado una variable miembro privada nombrada con un prefijo de guión bajo: _foo
.
Si la propiedad fue declarada readwrite
, Xcode genera un método getter llamado foo
y un setter llamado setFoo
. Estos métodos se invocan implícitamente cuando usa la notación de puntos (mi Object.myMethod). Si la propiedad fue declarada de readonly
, no se genera setter. Eso significa que la variable de respaldo, nombrada con el guión bajo, no es de solo lectura. El readonly
significa simplemente que no se sintetizó ningún método setter, y por lo tanto el uso de la notación de punto para establecer un valor falla con un error de compilación. La notación de punto falla porque el compilador le impide llamar a un método (el sistema) que no existe.
La forma más simple de evitar esto es acceder directamente a la variable miembro, nombrada con el guión bajo. ¡Puedes hacerlo incluso sin declarar esa variable con el guión bajo! Xcode está insertando esa declaración como parte del proceso de compilación / compilación, por lo que su código compilado tendrá la declaración de la variable. Pero nunca verá esa declaración en su archivo de código fuente original. No es magia, solo azúcar sintáctico .
Usar self->
es una forma de acceder a una variable miembro del objeto / instancia. Es posible que pueda omitir eso, y solo use el nombre var. Pero prefiero usar la flecha self + porque hace que mi código se auto-documente. Cuando veas el self->_foo
, sabes sin ambigüedad que _foo
es una variable miembro en esta instancia.
Por cierto, la discusión de los pros y los contras de los accesadores de propiedades versus el acceso directo a ivar es exactamente el tipo de tratamiento considerado que leerá en el libro de programación del Dr. Matt Neuberg para iOS . Me pareció muy útil leer y volver a leer.
Estás malinterpretando la otra pregunta. En esa pregunta hay una extensión de clase, declarada así:
@interface MYShapeEditorDocument ()
@property (readwrite, copy) NSArray *shapesInOrderBackToFront;
@end
Eso es lo que genera al colocador solo visible dentro de la implementación de la clase. Entonces, como dice Eiko, necesitas declarar una extensión de clase y anular la declaración de propiedad para decirle al compilador que genere un setter solo dentro de la clase.
La solución más corta es:
MyClass.h
@interface MyClass {
int myProperty;
}
@property (readonly) int myProperty;
@end
MyClass.h
@implementation MyClass
@synthesize myProperty;
@end
Necesitas decirle al compilador que también quieres un setter. Una forma común es ponerlo en una extensión de clase en el archivo .m:
@interface YourClass ()
@property (nonatomic, copy) NSString* eventDomain;
@end
Otra forma que he encontrado para trabajar con propiedades de solo lectura es usar @synthesize para especificar el almacén de respaldo. Por ejemplo
@interface MyClass
@property (readonly) int whatever;
@end
Luego en la implementación
@implementation MyClass
@synthesize whatever = _whatever;
@end
Sus métodos pueden establecer _whatever, ya que es una variable miembro.
Otra cosa interesante de la que me he dado cuenta en los últimos días es que puedes crear propiedades de solo lectura que se puedan escribir mediante subclases como las siguientes:
(en el archivo de encabezado)
@interface MyClass
{
@protected
int _propertyBackingStore;
}
@property (readonly) int myProperty;
@end
Luego, en la implementación
@synthesize myProperty = _propertyBackingStore;
Utilizará la declaración en el archivo de encabezado, por lo que las subclases pueden actualizar el valor de la propiedad, conservando su carácter de solo lectura.
Ligeramente lamentable en términos de ocultación de datos y encapsulamiento.
Si una propiedad se define como de solo lectura, eso significa que efectivamente no habrá un colocador que pueda usarse internamente para la clase o externamente de otras clases. (es decir: solo tendrá un "getter" si tiene sentido).
Por lo que dice, quiere una propiedad de lectura / escritura normal marcada como privada, que puede lograr estableciendo la variable de clase como privada en su archivo de interfaz como tal:
@private
NSString* eventDomain;
}