Objective-C: public, protected e private

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
@interface ClasseQualsiasi : NSObject
{
    int variabile; // @protected
}
@end

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 ClassB e 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.

Non ci sono commenti per questo Post

Lascia un commento

TAG XHTML PERMESSI: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> INSERIMENTO CODICE:
<pre></pre> // blocco generico
                   <code></code> // blocco generico
                   [cc_actionscript][/cc_actionscript] // Actionscript
                   [cc_actionscript3][/cc_actionscript3] // Actionscript 3
                   [cc_css][/cc_css] // CSS Style Sheet
                   [cc_html][/cc_html] // HTML
                   [cc_js][/cc_js] // Javascript
                   [cc_objc][/cc_objc] // Objective-C
                   [cc_php][/cc_objc] // PHP
                   [cc_sql][/cc_sql] // SQL