53

Sono piuttosto confuso su proprietà e variabili di istanza in Objective-C.Variabili di proprietà e istanze in Objective-C

Sono circa a metà di "Cocoa Programming for Mac OS X" di Aaron Hillegass e tutto è logico. Si potrebbe dichiarare una classe simile a questa:

@class Something; 

@interface MyClass : NSObject { 
    NSString *name; 
    NSArray *items; 

    Something *something; 

    IBOutlet NSTextField *myTextField; 
} 

@property (nonatomic, retain) NSString *name; 
@property (nonatomic, retain) NSArray *items; 
  • Poiché altri oggetti hanno bisogno di manipolare le nostre variabili di istanza name e items, usiamo @property/@synthesize per generare funzioni di accesso/mutatori per loro. All'interno della nostra classe, non usiamo gli accessor/mutators, ma interagiamo direttamente con la variabile di istanza.

  • something è solo una variabile di istanza che useremo nella nostra classe, e poiché nessun altro ha bisogno di usarlo, non creiamo una coppia di accessori e mutatori per questo.

  • Abbiamo bisogno di interagire con un campo di testo nella nostra interfaccia utente, quindi dichiariamo uno IBOutlet per esso, lo colleghiamo e il gioco è fatto.

Tutto molto logico.

Tuttavia, nel mondo iPhone, le cose sembrano essere diverse. Le persone dichiarano le proprietà per ogni singola variabile di istanza, dichiarano le proprietà per IBOutlets e utilizzano accessors/mutators per interagire con le variabili di istanza all'interno della classe (ad esempio scrivono [self setName:@"Test"] anziché name = @"Test").

Perché? Cosa sta succedendo? Queste differenze sono specifiche per iPhone? Quali sono i vantaggi di dichiarare le proprietà per tutte le variabili di istanza, dichiarare le proprietà per IBOutlets e utilizzare accessors/mutators all'interno della propria classe?

+1

Per chiunque in una situazione simile: oltre alle risposte qui sotto, vedere http://stackoverflow.com/questions/1221516/does-an-iboutlet-needs- to-be-a-property-synthesized e http://stackoverflow.com/questions/1250518/what-happens-if-i-dont-retain-iboutlet per il motivo per cui dichiareresti IBOutlet come proprietà. –

risposta

29

Nel mondo di iPhone, non è disponibile nessun garbage collector. Dovrai gestire attentamente la memoria con il conteggio dei riferimenti. Con questo in mente, prendere in considerazione la differenza tra:

name = @"Test"; 

e

self.name = @"Test"; 
// which is equivalent to: 
[self setName: @"Test"]; 

Se si imposta direttamente la variabile di istanza, senza previa considerazione, si perde il riferimento al valore precedente e si può Regola il conteggio dei ritiri (dovresti avere release d manualmente). Se accedi a una proprietà, questa verrà gestita automaticamente per te, insieme all'incremento del conteggio di conservazione dell'oggetto appena assegnato.

Il concetto fondamentale non è specifico per iPhone ma diventa fondamentale in un ambiente senza il garbage collector.

+0

... ma, in "Cocoa Programming per Mac OS X", Aaron Hillegass non usa Garbage Collector ... gestiamo la memoria manualmente ... –

+3

Steve: Non ho visto la guida. Ad ogni modo, se non si utilizzano le proprietà e si assegnano alle variabili di istanza * ovunque *, sia all'interno della classe o meno, è necessario fare attenzione ai conteggi dei riferimenti. Inoltre, i framework Mac OS X erano più vecchi delle controparti iPhone. Objective-C 2.0 era già presente quando l'iPhone SDK è diventato operativo, con un conseguente utilizzo più intenso delle proprietà rispetto a cui gli sviluppatori di OS X erano abituati. Nel complesso, è principalmente una questione di stile, piuttosto che una regola rigida. –

+0

@ Mehrdad: OK, questo ha senso. Grazie! –

6

Le proprietà sono utilizzate per generare accessor per variabili di istanza, non si verifica alcun evento magico.

È possibile implementare manualmente gli stessi accessor.

È possibile trovare nel libro di Aaron Hillegass esempi di 3 strategie di gestione della memoria per le variabili membro. Sono assign/copy/retain. Selezionate uno di quelli richiesti per una determinata variabile.

presumo si capisce la gestione della memoria in Objective-c ...

accessor nascondere la complessità e le differenze di gestione della memoria per ogni variabile.

Ad esempio:

name = @"Test" 

è un'assegnazione semplice, name ora detiene riferimento NSString @"Test". Tuttavia, è possibile decidere di utilizzare copy o retain. Non importa quale versione di gestione della memoria si è scelto di accesso nasconde la complessità e si accede sempre la variabile con (o simile):

[self setName:@"Test"] 
[self name] 

Ora setName: potrebbe usare assign/copy or retain e non si deve preoccupare.

La mia ipotesi è che le esercitazioni di iPhone utilizzino le proprietà per facilitare ai nuovi sviluppatori il passaggio attraverso la gestione della memoria (anche se è utile generare accessor appropriati con proprietà anziché implementarle manualmente ogni volta).

+0

OK, anche questo ha senso. Grazie! –

3

Tuttavia, nel mondo iPhone, le cose sembrano essere diverse. Le persone dichiarano le proprietà per ogni singola variabile di istanza, dichiarano le proprietà per IBOutlets e utilizzano accessors/mutators per interagire con le variabili di istanza all'interno della classe (ad esempio, scriverebbero [self setName:@"Test"] anziché name = @"Test").

Questo non è specifico per iPhone. Tranne nei metodi init e nel metodo dealloc, è buona norma utilizzare sempre i tuoi accessori. Il vantaggio principale, specialmente su Mac (con Cocoa Bindings), è che l'utilizzo dei tuoi accessor significa notifiche KVO gratuite.

Il motivo per cui le persone "dichiarano proprietà per ogni singola variabile di istanza" è molto probabilmente che tutte le loro variabili di istanza sono cose che desiderano esporre come proprietà. Se avessero qualcosa che vorrebbero mantenere privato, non dichiarerebbero una proprietà per esso nel file di intestazione. (Tuttavia, possono creare una proprietà per l'estensione di classe nel file di implementazione, al fine di ottenere le suddette notifiche KVO gratuite.)

A mio parere, dichiarare le proprietà degli outlet è eccessivo. Non ne vedo il senso. Se non si crea una proprietà, il caricatore pennino imposta la presa tramite accesso diretto a variabili di istanza, il che è perfetto per tale attività.

+0

Secondo http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW6, se non lo fai mantenere gli oggetti di livello superiore dopo aver caricato un pennino sull'iPhone, tali oggetti saranno deallocati. Su OS X gli oggetti di livello superiore del desktop hanno un conteggio di ritenzione pari a 1. Ma l'uso di proprietà (o almeno setter/getter) rende più chiaro ciò che sta accadendo, ed è soprattutto una buona idea su iPhone. –

+0

Sono d'accordo che rende esplicita la conservazione, ma in questo caso, non penso sia necessario, basti ricordare che uno sbocco è sempre una relazione di proprietà. –

+0

@Peter Hosey: Grazie! Tutto sta cominciando ad avere senso ora. Una domanda, però: perché non dovresti usare i tuoi metodi accessor/mutator nel metodo 'init'? –

2

Suggerirei che lo sviluppo moderno ha fatto un tentativo molto forte di identificare, definire e applicare le migliori pratiche.

Tra queste migliori pratiche troviamo continuità e coerenza.

Oltre a discutere su uso di accesso in init e dealloc metodi di accesso genere dovrebbe essere utilizzato tutto il tempo (all'interno e all'esterno di una classe) per i vantaggi che essi offrono, tra encapsulation, polimorfici implementazioni var (che sia permettono astrazione e refactoring) e per facilitare quelle migliori pratiche di continuità e coerenza. I benefici fondamentali di un linguaggio orientato agli oggetti entrano in gioco quando si fanno le cose in questo modo e sfruttando la pienezza delle capacità del linguaggio. Essere sempre coerenti nella propria codifica è un beneficio spesso sottovalutato, come in genere ogni programmatore anziano di solito attesta.

0

Si può scrivere come questo

//MyClass.h 

@class Something; 

@interface MyClass : NSObject 

@property (nonatomic, strong) NSString *name; 
@property (nonatomic, strong) NSArray *items; 

@end 

//MyClass.m 
@interface MyClass() 

@property (nonatomic, strong) IBOutlet NSTextField *myTextField; 
@property (nonatomic, strong) Something *something; 

@end 
Problemi correlati