¿Propiedades de Readonly en Objective-C?


He declarado una propiedad readonly en mi interfaz como tal:

 @property (readonly, nonatomic, copy) NSString* eventDomain;

Tal vez estoy malinterpretando las propiedades, pero pensé que cuando lo declaras como readonly, puedes usar el setter generado dentro del archivo de implementación (.m), pero las entidades externas no pueden cambiar el valor. Esta pregunta dice que eso es lo que debería suceder. Ese es el comportamiento que busco. Sin embargo, al intentar usar la sintaxis estándar setter o dot para establecer eventDomain dentro de mi método init, me da un error unrecognized selector sent to instance.. Por supuesto que estoy en la propiedad. Tratando de usarlo de esta manera:

 // inside one of my init methods
 [self setEventDomain:@"someString"]; // unrecognized selector sent to instance error

Así que estoy malinterpretando la readonly declaración sobre una propiedad? O es algo más?

Author: Community, 2011-01-03

7 answers

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
 108
Author: Eiko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-16 22:06:45

Otra forma que he encontrado para trabajar con las propiedades de readonly 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 que me he dado cuenta en los últimos días es que puede hacer propiedades de solo lectura que se pueden escribir por subclases como:

(en el archivo de cabecera)

@interface MyClass
{
    @protected
    int _propertyBackingStore;
}

@property (readonly) int myProperty;

@end

Entonces, en el aplicació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, mientras conservan su readonlyness.

Aunque lamentablemente en términos de ocultación y encapsulación de datos.

 35
Author: yano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-27 20:17:30

Eiko y otros dieron respuestas correctas.

Aquí hay una forma más sencilla: Acceder directamente a la variable miembro privado.

Ejemplo

En el encabezado .archivo h:

@property (strong, nonatomic, readonly) NSString* foo;

En la implementación .archivo m:

// 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 problemas, sin alboroto.

Detalles

A partir de Xcode 4.4 y LLVM Compiler 4.0 ( Nuevas características en Xcode 4.4 ), no es necesario meterse con el tareas discutidas en las otras respuestas:

  • La palabra clave synthesize
  • Declarando una variable
  • Volver a declarar la propiedad en la implementación .archivo m.

Después de declarar una propiedad foo, puede asumir que Xcode ha agregado una variable de miembro privado nombrada con un prefijo de guion 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 llaman implícitamente cuando utilice la notación de punto (mi objeto.myMethod). Si la propiedad fue declarada readonly, no se genera ningún setter. Eso significa que la variable de respaldo, nombrada con el subrayado, es no en sí misma. readonly significa simplemente que no se sintetizó ningún método setter, y por lo tanto usar la notación dot para establecer un valor falla con un error del compilador. La notación de puntos falla porque el compilador le impide llamar a un método (el setter) que no existe.

La forma más sencilla de evitar esto es para acceder directamente a la variable miembro, nombrada con el subrayado. ¡Puede hacerlo incluso sin declarar esa variable con nombre de subrayado! 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 variables. Pero nunca se ve esa declaración en el archivo de código fuente original. No magia, solo azúcar sintáctico .

Usar self-> es una forma de acceder a una variable miembro del objeto/instancia. Usted puede ser capaz de omita eso, y simplemente use el nombre var. Pero prefiero usar la flecha self + porque hace que mi código se documente automáticamente. Cuando ves 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 accesores de propiedad versus el acceso directo a ivar es exactamente el tipo de tratamiento reflexivo que leerá en el Dr. Matt Neuberg 's Programación de iOS libro. Me pareció muy útil leer y releer.

 32
Author: Basil Bourque,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-25 19:49:12

Consulte Personalizar las clases existentes en los documentos de iOS.

Solo leído Indica que la propiedad es de solo lectura. Si especifica readonly, solo se requiere un método getter en la implementación@. Si utiliza @ synthesize en el bloque de implementación, solo se sintetiza el método getter. Además, si intenta asignar un valor utilizando la sintaxis dot, se produce un error del compilador.

Las propiedades Readonly solo tienen un método getter. Todavía se puede establecer el respaldo ivar directamente dentro de la clase de la propiedad o usando codificación de valor clave.

 20
Author: Jonah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-25 14:37:55

Usted está 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 el setter 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.

 7
Author: BoltClock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:38

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
 4
Author: user2159978,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-03 13:17:56

Si una propiedad se define como readonly, eso significa que efectivamente no habrá un setter que se pueda usar internamente a la clase o externamente desde otras clases. (es decir: Solo tendrás un "getter" si eso tiene sentido.)

A partir de los sonidos de la misma, desea una propiedad normal de lectura/escritura que está marcada como privada, lo que puede lograr estableciendo la variable de clase como privada en su archivo de interfaz como tal:

@private
    NSString* eventDomain;
}
 2
Author: John Parker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-03 17:09:17