2010-10-28 10 views
7

Quiero entender cómo establecer los parámetros de las propiedades (acceso).initializer, properties, accessors y copy/retain/readonly

Tomé el siguiente código de un ejemplo del calendario Kal.

// Holiday.h 

@interface Holiday : NSObject 
{ 
    NSDate *date; 
    NSString *name; 
    NSString *country; 
} 

@property (nonatomic, retain, readonly) NSDate *date; 
@property (nonatomic, retain, readonly) NSString *name; 
@property (nonatomic, retain, readonly) NSString *country; 

- (id)initWithName:(NSString *)name country:(NSString *)country date:(NSDate *)date; 

@end 

// Holiday.m 

#import "Holiday.h" 

@implementation Holiday 

@synthesize date, name, country; 

- (id)initWithName:(NSString *)aName country:(NSString *)aCountry date:(NSDate *)aDate 
{ 
    if ((self = [super init])) { 
    name = [aName copy]; 
    country = [aCountry copy]; 
    date = [aDate retain]; 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    [date release]; 
    [name release]; 
    [country release]; 
    [super dealloc]; 
} 

@end 

1) Las propiedades se establecen en retain, pero dado que el colocador no se puede utilizar el retain aquí no tiene sentido.

2) Además, en el método initWithName los valores se establecen con copy. ¿Por qué no definir directamente las propiedades con copy y usar los métodos de acceso?

@property (nonatomic, copy) NSString *name; 
// ... 
self.name = aName; 

3) ¿Necesito el readonly aquí? No sé por qué se usan aquí. Si utilizo copy junto con el colocador, el readonly me prohíbe establecer el valor, porque no hay colocador.

4) En el método initWithName a veces se usa copy y a veces retain. Sugeriría utilizar siempre copy aquí, porque el valor no debería modificarse más tarde.

5) Lo que puedo recordar es que está bien a copy/retain en el initWithName y release en el método dealloc.

Entonces, ¿cómo sugeriría utilizar retain, copy y readonly en este ejemplo aquí?

Respuesta

13

ETA: @DougW señala correctamente que de una propiedad tipo de propiedad (assign/retain/copy) no afecta al comprador. Todavía afecta al colocador. Para los tipos readonly, esto importa si va a anular la parte readonly de la declaración en una extensión de clase, para que pueda usar el setter en su implementación. La anulación de propiedad de extensión de clase solo permite cambiar el estado readonly de la propiedad, por lo que el resto, es decir, la atomicidad y los tipos de propiedad, deben declararse adecuadamente en el encabezado. Incluso si no está anulando la propiedad ahora, podría hacerlo en el futuro, por lo que también podría documentar cómo desea que se administre la memoria utilizando la opción correcta para comenzar.

El recuento automático de referencias (ARC) cambia los detalles de implementación en tiempo de ejecución superponiendo sus propias reglas de administración de memoria además de las reglas de refcount clásicas, pero las reglas y consejos para configurar sus propiedades siguen siendo las mismas.


¿Por qué utilizar retain con readonly? Si marca una propiedad como retain, el descriptor de acceso sintetizado hace algo como esto:

/* getter for retain property */ 
- (NSString *)name { 
    return [[name retain] autorelease]; 
} 

Ahora bien, si el objeto que envió a -name cambia el nombre, mientras que todavía se esté usando, el código de llamada todavía tendrá una referencia válida a una cadena.Si lo declaró como assign, sin embargo, sería la siguiente:

/* getter for assign property */ 
- (NSString *)name { 
    return name; 
} 

Ahora, tan pronto como el nombre se cambió por el objeto, éste tendrá que ser puesto en libertad para evitar una fuga, lo que invalidará la llamada referencia del código El retain/copy/assign realmente establece una política de gestión de memoria: retain/copy dice: "Prometo que tengo una referencia al original/una copia del valor que proporciono aquí", mientras que assign dice: "Simplemente tengo el valor y reclamo no poseer referencia de esto ".

Cuando el valor no necesita gestión de memoria, como un simple int, entonces assign tiene sentido. Cuando intencionalmente no está reteniendo un objeto, como un delegado, entonces assign tiene sentido. Pero, en la mayoría de los demás casos, querrá retain o copy.

Además, el archivo de implementación solo puede anular la parte readwrite/readonly de una declaración de propiedad, no la parte de administración de memoria. Según lo declarado, el archivo puede tener .m:

setters para las declaraciones de bienes sobrescritos
@interface Holiday (/*class extension*/) 
@property(nonatomic, retain, readwrite) NSDate *date; 
/* override other properties to make them readwrite... */ 
@end 

no públicas luego serán sintetizados junto con los descriptores de acceso públicos.

¿Por qué no utilizar arregladores/accesorios durante -init? Dado que los programadores/accededores realizan con frecuencia la notificación KVO, que desea evitar mientras el objeto no está completamente inicializado, es decir, durante -init (cuando está medio iniciado en su camino a la inicialización completa) y -dealloc (cuando está semiautomatizado en su forma de ser completamente sin inicializar).

¿Por qué usar copy con readonly? Como respuesta a su primera pregunta: porque si copy versus retain contra assign afecta tanto a los instaladores como a los getters. Un captador de copia se vería así:

/* getter for copy property */ 
- (NSString *)name { 
    return [[name copy] autorelease]; 
} 

Por qué a veces ya veces copyretain?copy se usa generalmente con objetos de valor (objetos pasivos que representan un valor); retain se usa generalmente con otros objetos. Algunas veces, las preocupaciones sobre la eficiencia entran en juego (muy probablemente prematuramente ...), y puede optar por usar retain donde normalmente usaría copy.

¿Cómo usarías copy/retain junto con readonly aquí? Más o menos como lo hicieron. Anularía las declaraciones en una extensión de clase para que pueda usar setters para cambiar los valores de las propiedades fuera de -init y -dealloc, donde solo usaría el acceso directo a la variable de instancia. Lo haría también nil los Ivars después de la liberación de ellos en -dealloc, por ejemplo,

[name release], name = nil; 

Esto ayuda a evitar el envío de mensajes o de otra manera a hacer referencia a un objeto lanzado ya.

+0

** nonatomic ** propiedades de retención solo devuelve el puntero. Ellos ** no ** hacen lo de retener, lanzar automáticamente. Consulte la sección ** atomicity ** de los documentos http://developer.apple.com/library/ios/# documentation/cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html – JeremyP

+0

@JeremyP: Buena llamada. La decisión de no '[[foo retener] liberación automática] 'en accesadores no atómicos tiene sentido: si va a conservar el valor durante más tiempo que el ciclo de ciclo de ejecución actual, debe conservarlo usted mismo. Si está utilizando 'no atómico', básicamente está diciendo que la seguridad de la secuencia no es una preocupación. Si no tiene que preocuparse por la seguridad de las hebras, entonces no se ejecutará ningún código aparte del suyo mientras esté usando el valor devuelto por el descriptor de acceso, por lo que no es necesario que el descriptor de acceso ejecute '[[foo retener] liberación automática]' . –

+0

@Jeremy: iría tan lejos como para decir que el uso no atómico significa que está diciendo que la solidez no es una preocupación, o es una preocupación menor que el rendimiento. Establecer propiedades no atómicas sin un primer perfil de su código cuenta como una optimización prematura en mi libro. – JeremyP

Cuestiones relacionadas