2010-10-28 12 views
7

Voglio capire come impostare i parametri delle proprietà (accessors).inizializzatore, proprietà, accessori e copia/conserva/sola lettura

Ho preso il seguente codice da un esempio di 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) Le proprietà sono impostate retain, ma poiché il setter non può essere utilizzato il retain non ha senso qui.

2) Inoltre, nel metodo initWithName i valori sono impostati con copy. Perché non definire direttamente le proprietà con copy e utilizzare i metodi di accesso?

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

3) È necessario il readonly qui? Non so perché vengono usati qui. Se dovessi usare copy insieme al setter, il readonly mi proibisce di impostare il valore, perché non c'è un setter.

4) Nel metodo initWithName a volte viene utilizzato copy e talvolta retain. Vorrei suggerire di utilizzare sempre copy qui, perché il valore non dovrebbe essere modificato in seguito.

5) Quello che mi ricordo è che è OK per copy/retain nel initWithName e release nel metodo dealloc.

Quindi, come suggeriresti di utilizzare retain, copy e readonly in questo esempio qui?

risposta

13

ETA: @DougW fa giustamente notare che di una proprietà di tipo proprietario (assign/retain/copy) non influenza il getter. Influisce ancora sul setter. Per i tipi readonly, ciò è importante se si sostituisce la parte readonly della dichiarazione in un'estensione di classe, quindi è possibile utilizzare il setter all'interno dell'implementazione. Un override della proprietà dell'estensione di classe è consentito solo per modificare lo stato readonly della proprietà, quindi il resto di esso, ovvero i tipi di atomicità e proprietà, deve essere dichiarato in modo appropriato nell'intestazione. Anche se non stai sovrascrivendo la proprietà ora, potresti farlo in futuro, quindi potresti anche documentare come vuoi che la memoria sia gestita da te usando l'opzione corretta per cominciare.

Il conteggio dei riferimenti automatico (ARC) modifica i dettagli di implementazione del runtime sovrapponendo le proprie regole di gestione della memoria in aggiunta alle classiche regole del conteggio, ma le regole e i consigli per la configurazione delle proprietà rimangono invariati.


Perché usare retain con readonly? Se si contrassegna una proprietà come retain, la funzione di accesso sintetizzato fa qualcosa di simile:

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

Ora, se l'oggetto inviato -name ai cambiamenti del nome, mentre si sta ancora usando, il codice chiamante avrà ancora un riferimento valido a una stringa.Se ha dichiarato come assign, però, sarebbe come questo:

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

Ora, non appena il nome viene cambiato l'oggetto, dovrà essere rilasciato per evitare una perdita, invalidando la chiamata riferimento del codice. Lo/sta davvero affermando un criterio di gestione della memoria: retain/copy dice "Prometto che tengo un riferimento all'originale/una copia del valore che fornisco qui", mentre assign dice "Ho appena il valore e non rivendicare alcun riferimento proprietario a questo. "

Quando il valore non ha bisogno di gestione della memoria, ad esempio un semplice int, quindi assign ha senso. Se non stai mantenendo intenzionalmente un oggetto, ad esempio un delegato, allora assign ha senso. Ma, nella maggior parte degli altri casi, vorrai retain o copy.

Inoltre, il file di implementazione può sostituire solo la parte readwrite/readonly di una dichiarazione di proprietà, non la parte di gestione della memoria. Come dichiarato, il file può avere .m:

setter
@interface Holiday (/*class extension*/) 
@property(nonatomic, retain, readwrite) NSDate *date; 
/* override other properties to make them readwrite... */ 
@end 

non pubblici per le dichiarazioni di proprietà ignorati verranno poi sintetizzati insieme alle funzioni di accesso pubblici.

Perché non utilizzare setter/accessori durante -init? Perché setter/di accesso eseguono spesso notifica KVO, che si vuole evitare, mentre l'oggetto non è completamente inizializzato, cioè durante -init (quando è mezza inizializzato sul suo modo di piena inizializzazione) e -dealloc (quando è mezza inizializzato sul suo modo di essere completamente non inizializzato).

Perché utilizzare copy con readonly? Come in risposta alla sua prima domanda: perché se copy contro retain contro assign colpisce sia i setter ei getter. Un getter copia sarebbe simile a questa:

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

Perché a volte copy e talvolta retain?copy viene generalmente utilizzato con oggetti valore (oggetti passivi che rappresentano un valore); retain viene generalmente utilizzato con altri oggetti. A volte, i problemi di efficienza entrano in gioco (molto probabilmente prematuramente ...) e potresti scegliere di utilizzare retain dove normalmente utilizzerai lo copy.

Come utilizzare copy/retain insieme a readonly qui? Praticamente come loro. Sostituire le dichiarazioni in un'estensione di classe in modo da poter utilizzare i setter per modificare i valori delle proprietà al di fuori di -init e -dealloc, dove utilizzerei solo l'accesso diretto alle variabili d'istanza. Vorrei anche nil le ivars dopo di loro rilascio in -dealloc, per esempio,

[name release], name = nil; 

Questo permette di evitare l'invio di messaggi o altro riferimento a un oggetto già rilasciato.

+0

** le proprietà di ritenzione non anatomiche ** restituiscono solo il puntatore. Loro ** non fanno il fermo, cosa autorelease. Vedi la sezione ** atomicity ** dei documenti http://developer.apple.com/library/ios/# documentation/cacao/concettuale/oggettivoC/articoli/ocProperties.html – JeremyP

+0

@JeremyP: buona chiamata. La decisione di non "[autorizzare il [[foo retain] autorelease]' in accessors non atomici ha senso: se si manterrà il valore più a lungo del ciclo di runloop corrente, si dovrebbe tenerlo da solo. Se stai usando 'nonatomic', stai fondamentalmente affermando che la sicurezza del thread non è una preoccupazione. Se non ti devi preoccupare della sicurezza dei thread, allora nessun codice da parte tuo verrà eseguito mentre stai usando il valore di ritorno dalla funzione di accesso, quindi non è necessario che l'accessore esegua "[[foo retain] autorelease]' . –

+0

@Jeremy: Direi che usare mezzi non anatomici significa affermare che la robustezza non è una preoccupazione - o è meno preoccupante delle prestazioni. Impostare le proprietà su nonatomico senza prima profilare il codice conta come un'ottica prematura nel mio libro. – JeremyP

Problemi correlati