Skip to main content
Bright green leaf

Differenze tra Reference Type e Value Type in Swift

In Swift esistono 3 tipi di dati:

  1. Class
  2. Struct
  3. Enum

Class è un Reference Type mentre Struct e Enum sono Value Type.

La differenza fondamentale è che nel caso di Reference Type, n variabili (o costanti) possono referenziare la stessa istanza di una classe.

Nell’ambito dei Value Type invece, un valore può essere referenziato da una sola variabile (o costante).

Reference Type

Una classe è un Reference Type perché le istanze vengono accedute con la logica dei puntatori. Le istanze di una classe sono allocate nell’Heap mentre le variabili che le referenziano sono allocate nello Stack.

Più variabili possono referenziare contemporaneamente la stessa istanza di una classe.

Consideriamo la seguente classe.

Inizializziamo ora un’istanza di questa classe e assegniamola a una variabile.

La variabile show è una referenza, un puntatore. Questo significa che la variabile contiene l’indirizzo di memoria in cui è presente l’istanza di Show che abbiamo creato.

Creiamo ora una nuova variabile.

Con la linea precedente abbiamo copiato l’indirizzo di memoria contenuto in show nella nuova variabile anotherShow. E’ importante notare che sebbene ora abbiamo 2 variabili, entrambe referenziano lo stesso oggetto di tipo Show. Quindi se usiamo la prima per modificare un attributo dell’oggetto Show

la modifica avrà delle ripercussioni anche sulla seconda perché di fatto entrambe le variabili puntano alla stessa istanza di Show.

Chi conosce Objective-C, Java o altri linguaggi orientati agli oggetti troverà familiare questo concetto, andiamo avanti.

Value Type

Struct e Enum sono Value Type e vengono sempre salvate nello Stack. Questo li rende più velocemente accessibili.

Un valore di tipo Value Type viene sempre copiato durante un’assegnazione. Quindi non possono esistere 2 variabili associate allo stesso Value Type.

Il fatto che un Value Type viene sempre copiato durante un’assegnazione è vero a livello concettuale ma esiste un’ottimizzazione di basso livello (trasparente allo sviluppatore) chiamata Copy On Write che rende il comportamento reale leggermente diverso. La vedremo tra poco.

Trasformiamo la classe Show in una Struct.

E facciamo lo stesso test precedente.

Il risultato è differente. Questo perché alla riga #2 quando show viene assegnato a anotherShow, Swift crea una copia della Struct e le modifiche apportare a show non coinvolgeranno in alcun modo anotherShow.

Array, Dictionary e Set

In Swift Array, Dictionary e Set sono implementati tramite Struct (al contrario delle loro controparti Objective-C che invece sono di tipo Class).

Ancora una volta è facile notare che assegnando list a anotherList si produce una copia della Struct e da quel momento le operazioni effettuate su list non influenzano anotherList (e viceversa).

Copy on Write

La possibilità di poter creare dei Value Type è un ulteriore passo verso il paradigma della Functional Programming.

Tuttavia il fatto che ogni assegnazione di una variabile di tipo Struct produca una copia dell’intera Struct potrebbe sollevare perplessità sull’eccessiva occupazione in memoria di questo approccio.

Consideriamo questo codice.

Stiamo nuovamente definendo una Struct Show.

Alla riga #9 creiamo la variable show0 e la popoliamo con un valore di tipo Show.

E poi creiamo altre 9 variabili tutte popolate con il valore show0.

Secondo quanto detto in precedenza, ogni assegnazione (dalla riga #10 alla #19) duplica il valore show0, corretto?

Beh… no.

La verità è che Swift applica un meccanismo totalmente invisibile allo sviluppatore chiama Copy On Write in modo da ottimizzare la gestione della memoria.

Infatti se viene effettuata una semplice assegnazione di un Value Type

Swift invece di effettuare una copia (come avevamo detto) applica un approccio più simile a quello dei Reference Type, ovvero crea una referenza speciale e posticipa la copia effettiva fino a quando non è assolutamente necessario.

Dopo l’istruzione possiamo continuare a considerare concettualmente show1 un valore separato da show0. Ma a basso livello Swift sta trattando show0 e show1 usando l’approccio Reference Type, ovvero entrambe le variabili fanno riferimento alla stessa Struct.

Nel momento in cui andiamo a modificare un attributo di show0 (o show1) Swift decide che è giunto il momento di duplicare l’oggetto perché altrimenti le modifiche apportate a un valore andrebbero a influenzare anche l’altro.

Quindi poco prima di eseguire questa istruzione Swift duplica in memoria la Struct.

Conclusione

Una forma primitiva di Struct è presente in linguaggi non orientati agli oggetti come C. Objective-C, essendo definito come un sovrainsieme di C, ha automaticamente ereditato le Struct. Mentre altri linguaggi orientati agli oggetti, come Java, le hanno abbandonate in favore di un approccio totalmente orientato agli oggetti.

Swift segna un importante ritorno delle Struct che acquistano persino la possibilità di avere metodi e si rivelano (grazie all’approccio Value Type) particolarmente adatte alla Programmazione Funzionale.

Se qualcosa non vi è chiaro potete scrivere un commento, se invece pensate che il post sia utile potete condividerlo tramite i pulsanti di sharing qui sotto.

Trainer • Developer • Writer

Luca Angeletti

Trainer • Developer • Writer

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *