Scrissi un articolo simile per ActionScript tempo fa: Actionscript 3.0: public, protected, private e internal. Rileggendolo mi sono accorto di due cose: la prima è stata l’estrema somiglianza con Objective-C, anche se quest’ultimo non contempla internal. La seconda è che fui, all’epoca, assai conciso… errore che evito di ripetere adesso.
Dichiarazione di una classe
In Objective-C, come in altri linguaggi ad oggetti, è possibile specificare la visibilità di una o più variabili dichiarate nell’interfaccia. Objective-C propone 3 keyword per stabilire la visibilità di una variable:
1 2 3 | @public @protected @private |
Il loro uso non è obbligatorio. Tutte le variabili dichiarate nell’interfaccia di una classe sono, per default, @protected se non indicato diversamente.
1 2 3 4 5 |
Se scriviamo una classe che sarà usata per il subclassing, cioè una classe che eredita le sue caratteristiche da un’altra, le tre keyword sopra elencate diventano fondamentali; o quantomeno sarebbe buona norma usarle a dovere.
Vediamo con un esempio chiaro le differenze.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @interface ClassA : NSObject { @public int publicVar; @protected // Protected è il default int protectedVar; @private int privateVar; } @end @implementation ClassA - (id)init { if (self = [super init]) { publicVar = 2; protectedVar = 10; privateVar = 16; } return self; } @end |
Nell’esempio di sopra il metodo init vede e può impostare tutte e tre le variabili. Vediamo però cosa accade quando creiamo una classe ClassB che eredita da ClassA:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // ClassB è una sottoclasse di ClassA @interface ClassB : ClassA { @private int privateVarClassB; } @end @implementation ClassB - (id)init { if (self = [super init]) { // publicVar è accessibile da chiunque publicVar = 32; // Anche protectedVar è accessibile da ClassB, perché le variabili // @protected sono accessibili dalle sottoclassi protectedVar = 64; // privateVar genererà un ERRORE DI COMPILAZIONE, perché le variabili // @private NON sono accessibili alle sottoclassi privateVar = 128; // COMPILER ERROR HERE // privateVarClassB, ovviamente, sarà accessibili qui, in quanto // anche se definita "privata", non lo è per ClassB, che l'ha dichiarata privateVarClassB = 512; } return self; } @end |
Come ultimo esempio chiarificatore, vediamo cosa accade con l’istanza della classe:
Nota: adesso proveremo ad accedere alle ivar (variabili interne) usando l’istanza della classe
ClassBe non ereditandola. Questa è una differenza sostanziale e molto importante rispetto agli esempi precedenti, perché dimostra la vulnerabilità del codice se non si usano correttamente le keyword di visibilità
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // Definiamo una qualsiasi altra classe che usi al suo // interno un'istanza della ClassB sopra definita @interface ClassXYZ : NSObject { ClassB *classB; } @end @implementation ClassXYZ - (id)init { if (self = [super init]) { classB = [[ClassB alloc] init]; // Come sopra, publicVar era dichiarata "pubblica" e quindi // accessibile come ivar - notate l'indicatore "puntatore" -> classB->publicVar = 1024; // Invece, provando ad accedere alle ivar definite "protette" o // "private", il compilatore emetterà un ERRORE classB->protectedVar = 2048; classB->privateVar = 4096; classB->privateVarClassB = 8192; } return self; } @end |
Il metodo init riesce a modificare la ivar publicVar perché dichiarata all’interno di @public. Questo, di per sé, non è un errore ma è una procedura da valutare attentamente in quanto espone del codice a possibili infiltrazioni esterne non controllate! Le proprietà di una classe, infatti, hanno il duplice vantaggio di proteggere le ivar interne e verificare l’impostazione di una ivar dall’esterno.
Blindare il codice
Ecco alcune regole per essere sicuri di non inquinare le variabili di un oggetto involontariamente. Dichiarazione tipo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @interface ClassA : NSObject { @private int privateVarA; // Uso interno int _propertyA; // ivar per proprietà } @property int propertyA; @end @implementation ClassA @synthesize propertyA = _propertyA; - (id)init { self = [super init]; if(self) { privateVarA = 16384; // Uso interno _propertyA = 64; // Valore di default della proprietà propertyA } } @end |
Nessuno dall’esterno può accedere alle ivar dichiarate. L’unica ivar accessibile è _propertyA, tramite l’impostazione della proprietà propertyA. Avendo usato @synthesize propertyA = _propertyA; abbiamo ottenuto una mezza protezione, in quanto qualsiasi valore attributo alla proprietà propertyA si riflette automaticamente sulla ivar _propertyA. Tuttavia è possibile aggiungere facilmente una blindatura ulteriore inserendo nell’implementazione:
1 2 3 4 5 6 7 | // Sovrascrivo il setter di default creato da @synthesize - (void)setPropertyA:(int)aValue { // Imposto la ivar e quindi la proprietà solo a certe condizioni if(aValue >= 0 && aValue <= 512) { _propertyA = aValue; } } |
In questo modo abbiamo assicurato che la variabile interna ivar _propertyA (e quindi automaticamente la proprietà propertyA) avrà sempre valori compresi tra 0 e 512.
Sarebbe quindi sempre buona regala usare le ivar (spesso indicate con l’underscore davanti) in visibilità @private. Dove necessario, poi, scriversi i metodi di setter e getter, evitando l’uso del @synthesize.









7
Non ci sono commenti per questo Post
Lascia un commento