Lazy Load Property en iOS 5+ con ARC

Problema

Estoy migrando un código henetworkingado (pre iOS 5) donde carga perezoso algunas properties de readonly . Quiero actualizar este código a iOS 5+ con ARC. Pero acabo de aprender sobre ARC.

.marido

 @property (nonatomic, retain, readonly) NSDateFormatter *timeFormatter; 

.metro

 - (NSDateFormatter *)timeFormatter { if (timeFormatter == nil) { timeFormatter = [[NSDateFormatter alloc] init]; [timeFormatter setDateFormat:@"h:mm a"]; } return timeFormatter; } 

Lo que intenté

He intentado simplemente actualizar mi código, pero recibí una: Asignación a la propiedad readonly .

.marido

 @property (nonatomic, strong, readonly) NSDateFormatter *timeFormatter; 

.metro

 - (NSDateFormatter *)timeFormatter { if (self.timeFormatter == nil) { self.timeFormatter = [[NSDateFormatter alloc] init]; [self.timeFormatter setDateFormat:@"h:mm a"]; } return self.timeFormatter; } 

También revisé:

  • ios ARC fuerte y asignar
  • Subprocesamiento perezoso seguro en iOS
  • http://www.cocoanetics.com/2012/02/threadsafe-lazy-property-initialization/

Pregunta

¿Cuál es la forma correcta de cargar de forma perezosa una propiedad readonly en iOS 5+ con ARC? Apreciaría muestras de código para .h y .m .

Para un método getter personalizado (perezoso), debe acceder directamente a la variable de instancia (ya sea que use ARC o no). Entonces debes sintetizar la propiedad como

 @synthesize timeFormatter = _timeFormatter; 

Entonces tu método getter es

 - (NSDateFormatter *)timeFormatter { if (_timeFormatter == nil) { _timeFormatter = [[NSDateFormatter alloc] init]; [_timeFormatter setDateFormat:@"h:mm a"]; } return _timeFormatter; } 

Solo tiene que agregar algún mecanismo de synchronization si se accede a la propiedad desde múltiples subprocesss simultáneamente, eso también es independiente de ARC o no.

( Observación: las versiones Xcode más recientes pueden crear una sentencia @synthesize automáticamente y usar el prefijo de subrayado para las variables de instancia. Sin embargo, en este caso, ya que la propiedad es de solo lectura y proporciona un método getter, Xcode no sintetiza la propiedad automáticamente).

AÑADIDO: aquí hay un ejemplo de código completo para su conveniencia:

MyClass.h:

 #import <Foundation/Foundation.h> @interface MyClass : NSObject @property (nonatomic, strong, readonly) NSDateFormatter *timeFormatter; @end 

MyClass.m:

 #import "MyClass.h" @implementation MyClass @synthesize timeFormatter = _timeFormatter; - (NSDateFormatter *)timeFormatter { if (_timeFormatter == nil) { _timeFormatter = [[NSDateFormatter alloc] init]; [_timeFormatter setDateFormat:@"h:mm a"]; } return _timeFormatter; } @end 

MÁS INFORMACIÓN: De hecho, su método de timeFormatter ARC funciona sin cambios también con ARC, si la propiedad se sintetiza como

 @synthesize timeFormatter; // or: @synthesize timeFormatter = timeFormatter; 

El único "error" que timeFormatter fue replace timeFormatter por self.timeFormatter dentro del método getter. Esto crea dos problemas:

  • La lectura de self.timeFormatter dentro del método getter conduce a una recursión infinita.
  • Establecer self.timeFormatter no está permitido debido al atributo de solo lectura.

Entonces, si simplemente deja el método timeFormatter getter tal como estaba (usando la variable de instancia timeFormatter dentro del método), entonces también funciona con ARC.

Aún así, recomendaría prefijar variables de instancia para properties con un guión bajo como en el ejemplo de mi código, porque Xcode lo hace de la misma manera para las properties sintetizadas automáticamente.

(¡Espero que esto ayude y no aumente la confusión!)

Las properties de Readonly son solo eso: solo lectura. No debería haber participantes involucrados. La buena parte es que, si networkingeclara la variable en una extensión de class (por lo general con un par de paréntesis vacío), como readwrite (o simplemente elimine solo el file readonly), entonces puede asignarle dentro de .m, pero las classs que importarlo lo verá como readonly.

 @interface MyClass () @property (nonatomic, strong) NSDateFormatter *timeFormatter; @end 

Esta networkingeclaración permite una forma más limpia de acceder y mutar la propiedad internamente sin recurrir a la frágil síntesis de iVar (que se está convirtiendo en una antigüedad ahora que el comstackdor lo hace por ti). Puede seguir usando iVar como se muestra en la otra respuesta, pero no es necesario acceder a iVar fuera de -init o getters sintetizados. *

* Como Martin correctamente señaló, incluso si su asignación había tenido éxito, aún habría causado una recursión infinita, por lo que es necesario el acceso iVar, a less que usted declare explícitamente un getter, entonces puede usar el acceso a la propiedad.