Tutti le usano, ma forse non tutti sanno a cosa servono. Sono lì, sotto la dichiarazione delle nostre variabili di istanza, ci fanno compagnia fin dal primo “hello world” che abbiamo realizzato ed accompagnano silenziosamente il nostro codice a suon di (nonatomic, retain). Vediamo quindi cosa sono e come possiamo sfruttare appieno la potenza delle @property nei nostri programmi.
Iniziamo subito con una precisazione, le property sono facoltative, nessuno vi obbliga ad utilizzarle. Sono state introdotte con una oramai non più tanto recente release di objective-c per rendere meno noiosa la vita a noi programmatori, facendosi carico di scrivere per noi alcune porzioni di codice, quindi studiatele, capitele e poi decidete cosa fare di volta in volta.
Vi sarete accorti che le variabili di istanza di una classe (da ora ivar) non sono direttamente accessibili da altre classi, questo accade perché di default sono dichiarate come “protette”, creiamo ad esempio una nuova classe, che derivi direttamente da NSObject e dichiariamo una variabile di tipo stringa:
@interface Classe1 : NSObject {
@protected
NSSTring stringaDiProva;
}
@end
in questo caso l’attributo @protected è stato esplicitato, ma anche se non l’avessimo inserito la variabile sarebbe stata protetta di default.
Poichi sanno che le variabili possono essere dichiarate anche come @public ed in questo caso diventano automaticamente accessibili anche da altre classi, anche se con una sintassi poco comune in obj-c che è quella delle struct (nel corso di C vi avevo detto che gli oggetti sono in realtà delle struct, no?)
mioOggetto->stringaDiProva;
Ma come ogni buon programmatore sa bene, fornire accesso alle ivar è una pratica da evitare come la peste, perché la classe non può avere il controllo sui dati che questa variabile assume…facciamo un esempio: supponiamo che per qualche strano motivo io decida di memorizzare una data come ivar di una classe non come NSDate, ma come 3 interi separati,
int giorno
int mese
int anno
Quando qualcuno (anche io stesso) devo usare questa classe devo impostare “a mano” tutti e tre i valori usando un codice simile a questo:
mioOggetto->giorno = 1;
mioOggetto->mese = 5;
mioOggetto->anno = 2011;
//oppure anche così
mioOggetto->giorno = -1;
mioOggetto->mese = 55;
mioOggetto->anno = -45;
come vedete l’utente è libero di inserire qualsiasi dato e, cosa più brutta la mia classe non ha modo di accorgersene se non quando accederà a queste variabili.
Non sarebbe più comodo allora fornire tre metodi come questo?
-(void)setGiorno:(int)day {
if ((day > 0) && (day <= 31))
giorno = day;
}
else {
printf("giorno non valido");
}
oppure addirittura un unico metodo che faccia più o meno questo:
-(void)setDataWithGiorno:(int)day andMonth:(int)month andYear:(int)year {
if ((day == 31) && (month == 1))
giorno = day;
mese = month;
}
//altri test logici.....
}
Questa tecnica si chiama "incapsulamento" ed è uno dei pilastri fondamentali dell'OOP. (http://it.wikipedia.org/wiki/Programmazione_orientata_agli_oggetti#Incapsulamento)
Vi starete chiedendo "e questo che c'entra con le property?" c'entra eccome! Perché visto che l'incapsulamento è cosa buona e giusta, tutti i programmatori hanno preso l'abitudine di nascondere sempre le variabili di istanza e fornire sempre due metodi accessori (una italianizzazione di "accessor methods", peccato che "accessorio" in italiano non significhi "che accede")
Per esempio per una banale ivar di tipo int si scrive di solito:
-(int)nomeVariabile {
return nomeVariabile;
}
-(void)setNomeVariabile:(int)value {
nomeVariabile = value;
}
Nel caso di ivar di tipi più complessi, come gli oggetti dell'sdk di iOS, i metodi sono più corposi perché devono tenere conto anche del problema della memoria e del retaincount degli oggetti.
Questo, ad esempio, è il codice generato per una variabile di tipo NSString:
- (NSString *)miaStringa
{
return [[miaStringa retain] autorelease];
}
- (void)setMiaStringa:(NSString *)aMiaStringa
{
if (miaStringa != aMiaStringa) {
[aMiaStringa retain];
[miaStringa release];
miaStringa = aMiaStringa;
}
}
moltiplicate questo codice per una cinquantina di variabili a classe ed avrete un'idea della mole di codice monotono e ripetitivo che bisogna scrivere.
Esistono infatti anche programmi che lo fanno in automatico, dato il nome di una variabile e qualche altro parametro vi tirano fuori il codice esatto dei metodi accessori e questo è quello che fa anche il costrutto @proprerty ed il suo fratello @synthesize, che si occupano di creare, a partire da alcuni parametri posti tra le parentesi si property, gli opportuni metodi getter e setter.
Indipendentemente dai parametri utilizzati i nomi dei metodi seguono lo stesso standar che ho seguito io in questo articolo, il metodo getter ha lo stesso identico nome della ivar da accedere, mentre il setter si chiama setNomeVariabile dove NomeVariabile è stato scritto con la iniziale maiuscola.
Ma allora, visto che le ivar per le quali è stata anche dichiarata una property hanno gli accessor methods già prestabiliti non ci possiamo inventare una sintassi più facile questa?
[mioOggetto setNomeVariabileTemporanea:456];
Certo! Possiamo usare la notazione dotted, accedendo alla ivar tramite il codice:
mioOggetto.nomeVariabile = 42;
Attenzione però a non confondere questo codice con questo:
mioOggetto->nomeVariabile = 42;
Nel secondo caso, infatti, accediamo ad una variabile @public di una classe, mentre nel primo stiamo "sotto sotto" richiamanto il metodo setter generato per noi automaticamente dalla @property.
Vediamo infine quali sono le opzioni da fornire a @property affinché generi del codice secondo le nostre esigenze;
- nonatomic: Utile nel caso di programmazione concorrente, se viene specificato questo attributo i metodi getter e setter non saranno thread-safe. In breve, se la variabile associata a questa property viene utilizzata contemporaneamente da metodi che girano su thread separati omettete nonatomic e obj-c si occuperà di generare tutto il codice necessario per una gestione thread-safe.
- readonly: la property sarà valida in sola lettura, questo non si applica ovviamente all'accesso tramite gli accessor methods.
- readwrite: è il comporamento di default.
- assign: quando il valore viene settato non viene fatto nessun tipo di retain sulla variabile, il codice generato per il setter è questo:
-(void)setNomeVariabile:(NSString *)str { nomeVariabile = str; }
- copy: effettua una deep-copy dell'oggetto passato durante il set. il codice generato è questo:
-(void)setNomeVariabile:(NSString *)str { if(str != nomeVariabile) { [nomeVariabile release]; nomeVariabile = [str copy]; } }
- retain: forse il più usato, si limita a fare un retain dell'ogetto passato durante il set
-(void)setNomeVariabile:(NSString *)str { if(str != nomeVariabile) { [nomeVariabile release]; nomeVariabile = [str retain]; } }
Spero che questo articolo vi sia utile, non esitate a contattarmi sul forum per qualsiasi domanda.
IgnazioC
12 Responses to “L#018 – Objective-c: Le @property e altre amenità”
14 Giugno 2011
ByterosDichiarando dunque una variabile pubblica, posso accedere ad essa tramite questa notazione “->” da qualunque classe, evitando quindi di utilizzare il metodo Singleton o cose del genere?
Dove posso approfondire la notazione “->”?
Grazie, Byteros.
14 Giugno 2011
Simonec99Grazie per il Tutorial!
14 Giugno 2011
ignazioc‘azz lo sapevo che non la dovevo dire sta cosa 😉
Ho già detto che è da evitare come la peste, vero?
Si, comunque la risposta è si, le variabili pubbliche sono accessibili da tutte le altre classi, ricordati però che devi avere un riferimento (puntatore) all’istanza della classe, all’oggetto non puoi fare NOME_CLASSE->nomevariabile perché non ha senso, ma devi fare NOME_OGGETTO->nomevariabile.
c’è su questo sito un bellissimo, ottimo, perfetto, meraviglioso corso di C 😉 😉 in una delle ultime lezioni parla del costrutto struct, lì trovi info sull’operatore ->
ciao
15 Giugno 2011
ByterosIl C++ non è un problema, anche se quella notazione non l’ho mai usata. Effettivamente, oltre a violare l’incapsulamento, rendere pubbliche le variabiali è poco elegante, ma può comunque tornare utile.
Grazie mille.
20 Giugno 2011
_malza_…grande Ignazio!! il tuo articolo sulle proprietà è un alca selzer per il mio stomaco di programmatore, questo argomento proprio non lo digerivo invece ora…bruoaaaapp..opps pardon 🙂
21 Giugno 2011
lucagre..forte il digestivo hihi…cmq scusami ma sulle proprietà alcuni dubbi restano, del tipo: tu hai detto che se si dichiarasse pubblica una variabile di istanza l’utente potrebe andarla a settare come vuole creando potenzialmente anche caos poi nei metodi che la utilizzano…ok, ma comunque anche la mia proprietà poi l’utent può andare a settarmela come vuole creando caos…mi spiego meglio:
con variabile di istanza:
mioOggetto->giorno = -1; //no buono
con proprietà
-(int)giorno {
return giorno;
}
-(void)setgiorno:(int)value {
giorno = value;
}
quindi:
mioOggetto.giorno = -1 //quindi no buono lo stesso
alla fine cosa è cambiato??ho comunque settato a un valore potenzialmente pericoloso una variabile/proprietà…..cosa mi manca?
21 Giugno 2011
Ignaziocesatto non cambia molto. non puoi fare validazione del dato però è già un modo per incapsulare …. se domani decidessi di scrivere un setter particolare lo puoi fare mantenendo la compatibilità con la versione precedente.
21 Giugno 2011
Ignaziocrutto libero! !
21 Giugno 2011
lucagre…quindi se ho capito bene io potrei personalizzare il contenuto del codice all’interno del
-(void)setgiorno:(int)value {
giorno = value;
}
in modo da fare un controllo o altre cose, in pratica è questa la differenza con il rendere pubblica una variabile di istanza (a parte il fatto che non è trendy per un vero programmatore), ma all’ora tornando a bomba utilizzare @property associato con il @synthesize nel file di implementazione non serve a nulla visto che con queste due istruzioni ci pensa il compilatore a creare i metodi setter e getter e quindi li crea standard, e quindi senza il controllo sul dato..e quindi dichiarando @public la mia variabile giorno nella dichiarazione delle ivar ho alla fine lo stesso risultato……..e quindi in pratica è tutta una questione di formalità cioè: “non è belo dichiarare una variabile di istanza pubblica, al suo posto si può dichiarare una @property e sintetizzarla nel file dei metodi, certo se l’utente passa un valore errato alla mia proprietà non vi è controllo e quindi potenziale errore”….ma all’ora come mai nel tuo articolo leggo: “Ma come ogni buon programmatore sa bene, fornire accesso alle ivar è una pratica da evitare come la peste, perché la classe non può avere il controllo sui dati che questa variabile assume”, ok vero ma a questo punto anche con le proprietà…..svengo
21 Giugno 2011
ignaziocwait wait…non dimenticare che i metodi creati automaticamente dal syntetize si occupano anche della gestione della memoria con ratain/release..se metti tutto pubblico questo come lo fai?
22 Giugno 2011
lucagre..si in effetti avevo sorvolato la gestione della memoria che non è da poco, stò facendo un pò di prove su codice che mi stanno aiutando a chiarire il concetto 🙂
Grazie
15 Maggio 2012
Lucaio ho in AppDelegate.h:
#import
@interface AppDelegate : UIResponder {
int indexArray;
}
@property (strong, nonatomic) NSArray *pageInLanguage;
@property (strong, nonatomic) UIWindow *window;
@end
non so se ho settato bene gli attributi dell’array, il mio obiettivo e di accedere a questo array da un altra classe.
in ViewController.m, ho provato cosi, ma non funziona:
[[AppDelegate pageInLanguage] objectAtIndex:0];
come dovrei fare? : )