> For the complete documentation index, see [llms.txt](https://swift.unicorndonkeys.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://swift.unicorndonkeys.com/guida-al-linguaggio/proprieta.md).

# Proprietà (Properties)

Accedi a valori memorizzati e calcolati che fanno parte di un'istanza o di un tipo.

Le *proprietà* associano valori a una particolare classe, struttura o enumerazione. Le proprietà memorizzate memorizzano valori costanti e variabili come parte di un'istanza, mentre le proprietà calcolate calcolano (anziché memorizzare) un valore. Le proprietà calcolate sono fornite da classi, strutture ed enumerazioni. Le proprietà memorizzate sono fornite solo da classi e strutture.

Le proprietà memorizzate e calcolate sono di solito associate alle istanze di un tipo particolare. Tuttavia, le proprietà possono anche essere associate al tipo stesso. Tali proprietà sono note come proprietà di tipo.

Inoltre, puoi definire osservatori di proprietà per monitorare le modifiche al valore di una proprietà, a cui puoi rispondere con azioni personalizzate. Gli osservatori di proprietà possono essere aggiunti alle proprietà memorizzate che definisci tu stesso, e anche alle proprietà che una sottoclasse eredita dalla sua superclasse.

Puoi anche usare un property wrapper per riutilizzare il codice nel getter e nel setter di più proprietà.

## Proprietà memorizzate <a href="#proprieta-memorizzate" id="proprieta-memorizzate"></a>

Nella sua forma più semplice, una proprietà memorizzata è una costante o una variabile memorizzata come parte di un'istanza di una particolare classe o struttura. Le proprietà memorizzate possono essere *proprietà memorizzate variabili* (introdotte dalla parola chiave `var`) oppure *proprietà memorizzate costanti* (introdotte dalla parola chiave `let`).

Puoi fornire un valore predefinito per una proprietà memorizzata come parte della sua definizione, come descritto in [Valori predefiniti delle proprietà](/guida-al-linguaggio/inizializzazione.md#valori-predefiniti-delle-proprieta). Puoi anche impostare e modificare il valore iniziale di una proprietà memorizzata durante l'inizializzazione. Questo vale anche per le proprietà memorizzate costanti, come descritto in [Assegnare proprietà costanti durante l'inizializzazione](/guida-al-linguaggio/inizializzazione.md#assegnare-proprieta-costanti-durante-linizializzazione).

L'esempio seguente definisce una struttura chiamata `FixedLengthRange`, che descrive un intervallo di interi la cui lunghezza non può essere cambiata dopo la creazione:

```swift
struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// l'intervallo rappresenta i valori interi 0, 1 e 2
rangeOfThreeItems.firstValue = 6
// l'intervallo ora rappresenta i valori interi 6, 7 e 8
```

Le istanze di `FixedLengthRange` hanno una proprietà memorizzata variabile chiamata `firstValue` e una proprietà memorizzata costante chiamata `length`. Nell'esempio qui sopra, `length` viene inizializzata quando viene creato il nuovo intervallo e non può essere cambiata in seguito, perché è una proprietà costante.

### Proprietà memorizzate di istanze costanti di strutture <a href="#proprieta-memorizzate-di-istanze-costanti-di-strutture" id="proprieta-memorizzate-di-istanze-costanti-di-strutture"></a>

Se crei un'istanza di una struttura e assegni quell'istanza a una costante, non puoi modificare le proprietà dell'istanza, anche se sono state dichiarate come proprietà variabili:

```swift
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// questo intervallo rappresenta i valori interi 0, 1, 2 e 3
rangeOfFourItems.firstValue = 6
// questo segnalerà un errore, anche se firstValue è una proprietà variabile
```

Poiché `rangeOfFourItems` è dichiarata come costante (con la parola chiave `let`), non è possibile cambiare la sua proprietà `firstValue`, anche se `firstValue` è una proprietà variabile.

Questo comportamento è dovuto al fatto che le strutture sono *tipi valore*. Quando un'istanza di un tipo valore è contrassegnata come costante, lo sono anche tutte le sue proprietà.

Lo stesso non vale per le classi, che sono *tipi riferimento*. Se assegni un'istanza di un tipo riferimento a una costante, puoi comunque modificare le proprietà variabili di quell'istanza.

### Proprietà memorizzate lazy <a href="#proprieta-memorizzate-lazy" id="proprieta-memorizzate-lazy"></a>

Una *proprietà memorizzata lazy* è una proprietà il cui valore iniziale non viene calcolato fino alla prima volta in cui viene usata. Indichi una proprietà memorizzata lazy scrivendo il modificatore `lazy` prima della sua dichiarazione.

{% hint style="info" %}
**Nota:** Devi sempre dichiarare una proprietà lazy come variabile (con la parola chiave `var`), perché il suo valore iniziale potrebbe non essere recuperato fino a dopo il completamento dell'inizializzazione dell'istanza. Le proprietà costanti devono sempre avere un valore *prima* che l'inizializzazione sia completata, e quindi non possono essere dichiarate lazy.
{% endhint %}

Le proprietà lazy sono utili quando il valore iniziale di una proprietà dipende da fattori esterni i cui valori non sono noti fino a dopo il completamento dell'inizializzazione di un'istanza. Le proprietà lazy sono utili anche quando il valore iniziale di una proprietà richiede una configurazione complessa o computazionalmente costosa che non dovrebbe essere eseguita a meno che, o finché, non sia necessaria.

L'esempio seguente usa una proprietà memorizzata lazy per evitare l'inizializzazione non necessaria di una classe complessa. Questo esempio definisce due classi chiamate `DataImporter` e `DataManager`, nessuna delle quali è mostrata per intero:

```swift
class DataImporter {
    /*
    DataImporter è una classe per importare dati da un file esterno.
    Si presume che la classe richieda una quantità di tempo non trascurabile per l'inizializzazione.
    */
    var filename = "data.txt"
    // la classe DataImporter fornirebbe qui la funzionalità di importazione dei dati
}

class DataManager {
    lazy var importer = DataImporter()
    var data: [String] = []
    // la classe DataManager fornirebbe qui la funzionalità di gestione dei dati
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// l'istanza di DataImporter per la proprietà importer non è ancora stata creata
```

La classe `DataManager` ha una proprietà memorizzata chiamata `data`, che viene inizializzata con un nuovo array vuoto di valori `String`. Sebbene il resto della sua funzionalità non sia mostrato, lo scopo di questa classe `DataManager` è gestire e fornire accesso a questo array di dati `String`.

Parte della funzionalità della classe `DataManager` è la capacità di importare dati da un file. Questa funzionalità è fornita dalla classe `DataImporter`, per la quale si presume che l'inizializzazione richieda una quantità di tempo non trascurabile. Ciò potrebbe essere dovuto al fatto che un'istanza di `DataImporter` deve aprire un file e leggerne il contenuto in memoria quando l'istanza di `DataImporter` viene inizializzata.

Poiché è possibile che un'istanza di `DataManager` gestisca i suoi dati senza mai importare dati da un file, `DataManager` non crea una nuova istanza di `DataImporter` quando viene creato il `DataManager` stesso. Ha invece più senso creare l'istanza di `DataImporter` se e quando viene usata per la prima volta.

Poiché è contrassegnata con il modificatore `lazy`, l'istanza di `DataImporter` per la proprietà `importer` viene creata solo quando si accede per la prima volta alla proprietà `importer`, per esempio quando viene interrogata la sua proprietà `filename`:

```swift
print(manager.importer.filename)
// l'istanza di DataImporter per la proprietà importer è stata ora creata
// Stampa "data.txt".
```

{% hint style="info" %}
**Nota:** Se a una proprietà contrassegnata con il modificatore `lazy` accedono più thread contemporaneamente e la proprietà non è ancora stata inizializzata, non c'è alcuna garanzia che la proprietà venga inizializzata una sola volta.
{% endhint %}

### Proprietà memorizzate e variabili di istanza <a href="#proprieta-memorizzate-e-variabili-di-istanza" id="proprieta-memorizzate-e-variabili-di-istanza"></a>

Se hai esperienza con Objective-C, potresti sapere che esso fornisce *due* modi per memorizzare valori e riferimenti come parte di un'istanza di classe. Oltre alle proprietà, puoi usare le variabili di istanza come memoria di supporto per i valori memorizzati in una proprietà.

Swift unifica questi concetti in un'unica dichiarazione di proprietà. Una proprietà Swift non ha una variabile di istanza corrispondente, e alla memoria di supporto di una proprietà non si accede direttamente. Questo approccio evita confusione su come si accede al valore in contesti diversi e semplifica la dichiarazione della proprietà in un'unica istruzione definitiva. Tutte le informazioni sulla proprietà — compresi il nome, il tipo e le caratteristiche di gestione della memoria — sono definite in un unico punto come parte della definizione del tipo.

## Proprietà calcolate <a href="#proprieta-calcolate" id="proprieta-calcolate"></a>

Oltre alle proprietà memorizzate, classi, strutture ed enumerazioni possono definire *proprietà calcolate*, che in realtà non memorizzano un valore. Forniscono invece un getter e un setter opzionale per recuperare e impostare indirettamente altre proprietà e valori.

```swift
struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
    size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
// initialSquareCenter si trova in (5.0, 5.0)
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// Stampa "square.origin is now at (10.0, 10.0)".
```

Questo esempio definisce tre strutture per lavorare con forme geometriche:

* `Point` incapsula le coordinate x e y di un punto.
* `Size` incapsula una larghezza (`width`) e un'altezza (`height`).
* `Rect` definisce un rettangolo tramite un punto di origine e una dimensione.

La struttura `Rect` fornisce anche una proprietà calcolata chiamata `center`. La posizione corrente del centro di un `Rect` può sempre essere determinata dai suoi `origin` e `size`, e quindi non hai bisogno di memorizzare il punto centrale come valore `Point` esplicito. Invece, `Rect` definisce un getter e un setter personalizzati per una variabile calcolata chiamata `center`, per permetterti di lavorare con il `center` del rettangolo come se fosse una vera proprietà memorizzata.

L'esempio qui sopra crea una nuova variabile `Rect` chiamata `square`. La variabile `square` viene inizializzata con un punto di origine `(0, 0)`, e una larghezza e un'altezza pari a `10`. Questo quadrato è rappresentato dal quadrato verde chiaro nel diagramma seguente.

Alla proprietà `center` della variabile `square` si accede quindi tramite la sintassi a punto (`square.center`), il che fa sì che venga chiamato il getter di `center`, per recuperare il valore corrente della proprietà. Invece di restituire un valore esistente, il getter calcola effettivamente e restituisce un nuovo `Point` per rappresentare il centro del quadrato. Come si può vedere sopra, il getter restituisce correttamente un punto centrale `(5, 5)`.

La proprietà `center` viene poi impostata a un nuovo valore `(15, 15)`, il che sposta il quadrato in alto e a destra, nella nuova posizione mostrata dal quadrato verde scuro nel diagramma seguente. Impostare la proprietà `center` chiama il setter di `center`, che modifica i valori `x` e `y` della proprietà memorizzata `origin`, e sposta il quadrato nella sua nuova posizione.

![](https://3581893755-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNzatREGwodw9Z93LQYJb%2Fuploads%2Fqaoej0L3jbUDn5UNJY5z%2FcomputedProperties%402x.png?alt=media)

### Dichiarazione abbreviata del setter <a href="#dichiarazione-abbreviata-del-setter" id="dichiarazione-abbreviata-del-setter"></a>

Se il setter di una proprietà calcolata non definisce un nome per il nuovo valore da impostare, viene usato il nome predefinito `newValue`. Ecco una versione alternativa della struttura `Rect` che si avvale di questa notazione abbreviata:

```swift
struct AlternativeRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}
```

### Dichiarazione abbreviata del getter <a href="#dichiarazione-abbreviata-del-getter" id="dichiarazione-abbreviata-del-getter"></a>

Se l'intero corpo di un getter è una singola espressione, il getter restituisce implicitamente quell'espressione. Ecco un'altra versione della struttura `Rect` che si avvale di questa notazione abbreviata e della notazione abbreviata per i setter:

```swift
struct CompactRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            Point(x: origin.x + (size.width / 2),
                  y: origin.y + (size.height / 2))
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}
```

Omettere il `return` da un getter segue le stesse regole dell'omissione di `return` da una funzione, come descritto in [Funzioni con un return implicito](/guida-al-linguaggio/funzioni.md#funzioni-con-un-return-implicito).

### Proprietà calcolate di sola lettura <a href="#proprieta-calcolate-di-sola-lettura" id="proprieta-calcolate-di-sola-lettura"></a>

Una proprietà calcolata con un getter ma senza setter è nota come *proprietà calcolata di sola lettura*. Una proprietà calcolata di sola lettura restituisce sempre un valore, e vi si può accedere tramite la sintassi a punto, ma non può essere impostata a un valore diverso.

{% hint style="info" %}
**Nota:** Devi dichiarare le proprietà calcolate — comprese le proprietà calcolate di sola lettura — come proprietà variabili con la parola chiave `var`, perché il loro valore non è fisso. La parola chiave `let` si usa solo per le proprietà costanti, per indicare che i loro valori non possono essere cambiati una volta impostati come parte dell'inizializzazione dell'istanza.
{% endhint %}

Puoi semplificare la dichiarazione di una proprietà calcolata di sola lettura rimuovendo la parola chiave `get` e le sue parentesi graffe:

```swift
struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
        return width * height * depth
    }
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// Stampa "the volume of fourByFiveByTwo is 40.0".
```

Questo esempio definisce una nuova struttura chiamata `Cuboid`, che rappresenta una scatola rettangolare 3D con le proprietà `width`, `height` e `depth`. Questa struttura ha anche una proprietà calcolata di sola lettura chiamata `volume`, che calcola e restituisce il volume corrente del parallelepipedo. Non ha senso che `volume` sia impostabile, perché sarebbe ambiguo quali valori di `width`, `height` e `depth` dovrebbero essere usati per un particolare valore di `volume`. Ciononostante, è utile che un `Cuboid` fornisca una proprietà calcolata di sola lettura per permettere agli utenti esterni di scoprire il suo volume calcolato corrente.

## Osservatori di proprietà <a href="#osservatori-di-proprieta" id="osservatori-di-proprieta"></a>

Gli osservatori di proprietà osservano le modifiche al valore di una proprietà e vi rispondono. Gli osservatori di proprietà vengono chiamati ogni volta che il valore di una proprietà viene impostato, anche se il nuovo valore è uguale al valore corrente della proprietà.

Puoi aggiungere osservatori di proprietà nei punti seguenti:

* Proprietà memorizzate che definisci
* Proprietà memorizzate che erediti
* Proprietà calcolate che erediti

Per una proprietà ereditata, aggiungi un osservatore di proprietà eseguendo l'override di quella proprietà in una sottoclasse. Per una proprietà calcolata che definisci, usa il setter della proprietà per osservare le modifiche del valore e rispondervi, invece di cercare di creare un osservatore. L'override delle proprietà è descritto in [Override](/guida-al-linguaggio/ereditarieta.md#override).

Hai la possibilità di definire uno o entrambi questi osservatori su una proprietà:

* `willSet` viene chiamato subito prima che il valore venga memorizzato.
* `didSet` viene chiamato immediatamente dopo che il nuovo valore è stato memorizzato.

Se implementi un osservatore `willSet`, gli viene passato il nuovo valore della proprietà come parametro costante. Puoi specificare un nome per questo parametro come parte della tua implementazione di `willSet`. Se non scrivi il nome del parametro e le parentesi all'interno della tua implementazione, il parametro viene reso disponibile con il nome di parametro predefinito `newValue`.

Analogamente, se implementi un osservatore `didSet`, gli viene passato un parametro costante contenente il vecchio valore della proprietà. Puoi dare un nome al parametro o usare il nome di parametro predefinito `oldValue`. Se assegni un valore a una proprietà all'interno del suo stesso osservatore `didSet`, il nuovo valore che assegni sostituisce quello appena impostato.

{% hint style="info" %}
**Nota:** Gli osservatori `willSet` e `didSet` delle proprietà della superclasse vengono chiamati quando una proprietà viene impostata nell'inizializzatore di una sottoclasse, dopo che è stato chiamato l'inizializzatore della superclasse. Non vengono chiamati mentre una classe imposta le proprie proprietà nel corpo dell'inizializzatore.

Per maggiori informazioni sulla delega degli inizializzatori, vedi [Delega degli inizializzatori per i tipi valore](/guida-al-linguaggio/inizializzazione.md#delega-degli-inizializzatori-per-i-tipi-valore) e [Delega degli inizializzatori per i tipi classe](/guida-al-linguaggio/inizializzazione.md#delega-degli-inizializzatori-per-i-tipi-classe).
{% endhint %}

Ecco un esempio di `willSet` e `didSet` in azione. L'esempio seguente definisce una nuova classe chiamata `StepCounter`, che tiene traccia del numero totale di passi che una persona compie mentre cammina. Questa classe potrebbe essere usata con i dati in input di un pedometro o di un altro contapassi per tenere traccia dell'esercizio fisico di una persona durante la sua routine quotidiana.

```swift
class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
```

La classe `StepCounter` dichiara una proprietà `totalSteps` di tipo `Int`. Si tratta di una proprietà memorizzata con osservatori `willSet` e `didSet`.

Gli osservatori `willSet` e `didSet` di `totalSteps` vengono chiamati ogni volta che alla proprietà viene assegnato un nuovo valore. Questo vale anche se il nuovo valore è uguale al valore corrente.

L'osservatore `willSet` di questo esempio usa un nome di parametro personalizzato, `newTotalSteps`, per il nuovo valore in arrivo. In questo esempio, si limita a stampare il valore che sta per essere impostato.

L'osservatore `didSet` viene chiamato dopo che il valore di `totalSteps` è stato aggiornato. Confronta il nuovo valore di `totalSteps` con il vecchio valore. Se il numero totale di passi è aumentato, viene stampato un messaggio per indicare quanti nuovi passi sono stati compiuti. L'osservatore `didSet` non fornisce un nome di parametro personalizzato per il vecchio valore, e viene invece usato il nome predefinito `oldValue`.

{% hint style="info" %}
**Nota:** Se passi una proprietà che ha degli osservatori a una funzione come parametro in-out, gli osservatori `willSet` e `didSet` vengono sempre chiamati. Questo è dovuto al modello di memoria copy-in copy-out dei parametri in-out: il valore viene sempre riscritto nella proprietà alla fine della funzione. Per una discussione dettagliata del comportamento dei parametri in-out, vedi [Parametri in-out](/riferimento-del-linguaggio/dichiarazioni.md#parametri-in-out).
{% endhint %}

## Property wrapper <a href="#property-wrapper" id="property-wrapper"></a>

Un property wrapper aggiunge un livello di separazione tra il codice che gestisce il modo in cui una proprietà viene memorizzata e il codice che definisce una proprietà. Per esempio, se hai proprietà che forniscono controlli di thread safety o memorizzano i loro dati sottostanti in un database, devi scrivere quel codice per ogni proprietà. Quando usi un property wrapper, scrivi il codice di gestione una volta sola quando definisci il wrapper, e poi riutilizzi quel codice di gestione applicandolo a più proprietà.

Per definire un property wrapper, crei una struttura, un'enumerazione o una classe che definisce una proprietà `wrappedValue`. Nel codice seguente, la struttura `TwelveOrLess` garantisce che il valore che racchiude contenga sempre un numero minore o uguale a 12. Se le chiedi di memorizzare un numero più grande, memorizza invece 12.

```swift
@propertyWrapper
struct TwelveOrLess {
    private var number = 0
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}
```

Il setter garantisce che i nuovi valori siano minori o uguali a 12, e il getter restituisce il valore memorizzato.

{% hint style="info" %}
**Nota:** La dichiarazione di `number` nell'esempio qui sopra contrassegna la variabile come `private`, il che garantisce che `number` venga usata solo nell'implementazione di `TwelveOrLess`. Il codice scritto in qualsiasi altro punto accede al valore usando il getter e il setter di `wrappedValue`, e non può usare `number` direttamente. Per informazioni su `private`, vedi [Controllo degli accessi](/guida-al-linguaggio/controllo-degli-accessi.md).
{% endhint %}

Applichi un wrapper a una proprietà scrivendo il nome del wrapper prima della proprietà come attributo. Ecco una struttura che memorizza un rettangolo e che usa il property wrapper `TwelveOrLess` per garantire che le sue dimensioni siano sempre pari o inferiori a 12:

```swift
struct SmallRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}

var rectangle = SmallRectangle()
print(rectangle.height)
// Stampa "0".

rectangle.height = 10
print(rectangle.height)
// Stampa "10".

rectangle.height = 24
print(rectangle.height)
// Stampa "12".
```

Le proprietà `height` e `width` ottengono i loro valori iniziali dalla definizione di `TwelveOrLess`, che imposta `TwelveOrLess.number` a zero. Il setter di `TwelveOrLess` considera 10 un valore valido, quindi la memorizzazione del numero 10 in `rectangle.height` avviene come scritto. Tuttavia, 24 è più grande di quanto `TwelveOrLess` consenta, quindi tentare di memorizzare 24 finisce per impostare `rectangle.height` a 12, il valore più grande consentito.

Quando applichi un wrapper a una proprietà, il compilatore sintetizza il codice che fornisce la memoria per il wrapper e il codice che fornisce l'accesso alla proprietà attraverso il wrapper. (Il property wrapper è responsabile della memorizzazione del valore racchiuso, quindi per questo non c'è codice sintetizzato.) Potresti scrivere codice che usa il comportamento di un property wrapper senza avvalerti della speciale sintassi degli attributi. Per esempio, ecco una versione di `SmallRectangle` del listato di codice precedente che racchiude esplicitamente le sue proprietà nella struttura `TwelveOrLess`, invece di scrivere `@TwelveOrLess` come attributo:

```swift
struct SmallRectangle {
    private var _height = TwelveOrLess()
    private var _width = TwelveOrLess()
    var height: Int {
        get { return _height.wrappedValue }
        set { _height.wrappedValue = newValue }
    }
    var width: Int {
        get { return _width.wrappedValue }
        set { _width.wrappedValue = newValue }
    }
}
```

Le proprietà `_height` e `_width` memorizzano un'istanza del property wrapper, `TwelveOrLess`. Il getter e il setter di `height` e `width` racchiudono l'accesso alla proprietà `wrappedValue`.

### Impostare i valori iniziali delle proprietà racchiuse <a href="#impostare-i-valori-iniziali-delle-proprieta-racchiuse" id="impostare-i-valori-iniziali-delle-proprieta-racchiuse"></a>

Il codice degli esempi qui sopra imposta il valore iniziale della proprietà racchiusa dando a `number` un valore iniziale nella definizione di `TwelveOrLess`. Il codice che usa questo property wrapper non può specificare un valore iniziale diverso per una proprietà racchiusa da `TwelveOrLess` — per esempio, la definizione di `SmallRectangle` non può dare valori iniziali a `height` o `width`. Per supportare l'impostazione di un valore iniziale o altre personalizzazioni, il property wrapper deve aggiungere un inizializzatore. Ecco una versione ampliata di `TwelveOrLess` chiamata `SmallNumber` che definisce inizializzatori che impostano il valore racchiuso e il valore massimo:

```swift
@propertyWrapper
struct SmallNumber {
    private var maximum: Int
    private var number: Int

    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, maximum) }
    }

    init() {
        maximum = 12
        number = 0
    }
    init(wrappedValue: Int) {
        maximum = 12
        number = min(wrappedValue, maximum)
    }
    init(wrappedValue: Int, maximum: Int) {
        self.maximum = maximum
        number = min(wrappedValue, maximum)
    }
}
```

La definizione di `SmallNumber` include tre inizializzatori — `init()`, `init(wrappedValue:)` e `init(wrappedValue:maximum:)` — che gli esempi seguenti usano per impostare il valore racchiuso e il valore massimo. Per informazioni sull'inizializzazione e sulla sintassi degli inizializzatori, vedi [Inizializzazione](/guida-al-linguaggio/inizializzazione.md).

Quando applichi un wrapper a una proprietà e non specifichi un valore iniziale, Swift usa l'inizializzatore `init()` per configurare il wrapper. Per esempio:

```swift
struct ZeroRectangle {
    @SmallNumber var height: Int
    @SmallNumber var width: Int
}

var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// Stampa "0 0".
```

Le istanze di `SmallNumber` che racchiudono `height` e `width` vengono create chiamando `SmallNumber()`. Il codice all'interno di quell'inizializzatore imposta il valore racchiuso iniziale e il valore massimo iniziale, usando i valori predefiniti zero e 12. Il property wrapper fornisce ancora tutti i valori iniziali, come nell'esempio precedente che usava `TwelveOrLess` in `SmallRectangle`. A differenza di quell'esempio, `SmallNumber` supporta anche la scrittura di quei valori iniziali come parte della dichiarazione della proprietà.

Quando specifichi un valore iniziale per la proprietà, Swift usa l'inizializzatore `init(wrappedValue:)` per configurare il wrapper. Per esempio:

```swift
struct UnitRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber var width: Int = 1
}

var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)
// Stampa "1 1".
```

Quando scrivi `= 1` su una proprietà con un wrapper, questo viene tradotto in una chiamata all'inizializzatore `init(wrappedValue:)`. Le istanze di `SmallNumber` che racchiudono `height` e `width` vengono create chiamando `SmallNumber(wrappedValue: 1)`. L'inizializzatore usa il valore racchiuso specificato qui, e usa il valore massimo predefinito di 12.

Quando scrivi degli argomenti tra parentesi dopo l'attributo personalizzato, Swift usa l'inizializzatore che accetta quegli argomenti per configurare il wrapper. Per esempio, se fornisci un valore iniziale e un valore massimo, Swift usa l'inizializzatore `init(wrappedValue:maximum:)`:

```swift
struct NarrowRectangle {
    @SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
    @SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
}

var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
// Stampa "2 3".

narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
// Stampa "5 4".
```

L'istanza di `SmallNumber` che racchiude `height` viene creata chiamando `SmallNumber(wrappedValue: 2, maximum: 5)`, e l'istanza che racchiude `width` viene creata chiamando `SmallNumber(wrappedValue: 3, maximum: 4)`.

Includendo argomenti per il property wrapper, puoi impostare lo stato iniziale nel wrapper o passare altre opzioni al wrapper quando viene creato. Questa sintassi è il modo più generale di usare un property wrapper. Puoi fornire all'attributo tutti gli argomenti di cui hai bisogno, e questi vengono passati all'inizializzatore.

Quando includi argomenti del property wrapper, puoi anche specificare un valore iniziale usando l'assegnamento. Swift tratta l'assegnamento come un argomento `wrappedValue` e usa l'inizializzatore che accetta gli argomenti che includi. Per esempio:

```swift
struct MixedRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber(maximum: 9) var width: Int = 2
}

var mixedRectangle = MixedRectangle()
print(mixedRectangle.height)
// Stampa "1".

mixedRectangle.height = 20
print(mixedRectangle.height)
// Stampa "12".
```

L'istanza di `SmallNumber` che racchiude `height` viene creata chiamando `SmallNumber(wrappedValue: 1)`, che usa il valore massimo predefinito di 12. L'istanza che racchiude `width` viene creata chiamando `SmallNumber(wrappedValue: 2, maximum: 9)`.

### Proiettare un valore da un property wrapper <a href="#proiettare-un-valore-da-un-property-wrapper" id="proiettare-un-valore-da-un-property-wrapper"></a>

Oltre al valore racchiuso, un property wrapper può esporre funzionalità aggiuntive definendo un *valore proiettato* — per esempio, un property wrapper che gestisce l'accesso a un database può esporre un metodo `flushDatabaseConnection()` sul suo valore proiettato. Il nome del valore proiettato è lo stesso del valore racchiuso, tranne per il fatto che inizia con il simbolo del dollaro (`$`). Poiché il tuo codice non può definire proprietà che iniziano con `$`, il valore proiettato non interferisce mai con le proprietà che definisci.

Nell'esempio `SmallNumber` visto sopra, se provi a impostare la proprietà a un numero troppo grande, il property wrapper corregge il numero prima di memorizzarlo. Il codice seguente aggiunge una proprietà `projectedValue` alla struttura `SmallNumber` per tenere traccia del fatto che il property wrapper abbia corretto o meno il nuovo valore della proprietà prima di memorizzarlo.

```swift
@propertyWrapper
struct SmallNumber {
    private var number: Int
    private(set) var projectedValue: Bool

    var wrappedValue: Int {
        get { return number }
        set {
            if newValue > 12 {
                number = 12
                projectedValue = true
            } else {
                number = newValue
                projectedValue = false
            }
        }
    }

    init() {
        self.number = 0
        self.projectedValue = false
    }
}
struct SomeStructure {
    @SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()

someStructure.someNumber = 4
print(someStructure.$someNumber)
// Stampa "false".

someStructure.someNumber = 55
print(someStructure.$someNumber)
// Stampa "true".
```

Scrivere `someStructure.$someNumber` accede al valore proiettato del wrapper. Dopo aver memorizzato un numero piccolo come quattro, il valore di `someStructure.$someNumber` è `false`. Tuttavia, il valore proiettato è `true` dopo aver tentato di memorizzare un numero troppo grande, come 55.

Un property wrapper può restituire un valore di qualsiasi tipo come suo valore proiettato. In questo esempio, il property wrapper espone una sola informazione — se il numero è stato corretto o meno — quindi espone quel valore booleano come suo valore proiettato. Un wrapper che ha bisogno di esporre più informazioni può restituire un'istanza di un altro tipo, oppure può restituire `self` per esporre l'istanza del wrapper come suo valore proiettato.

Quando accedi a un valore proiettato da codice che fa parte del tipo, come il getter di una proprietà o un metodo di istanza, puoi omettere `self.` prima del nome della proprietà, proprio come quando accedi ad altre proprietà. Il codice dell'esempio seguente fa riferimento al valore proiettato del wrapper attorno a `height` e `width` come `$height` e `$width`:

```swift
enum Size {
    case small, large
}

struct SizedRectangle {
    @SmallNumber var height: Int
    @SmallNumber var width: Int

    mutating func resize(to size: Size) -> Bool {
        switch size {
        case .small:
            height = 10
            width = 20
        case .large:
            height = 100
            width = 100
        }
        return $height || $width
    }
}
```

Poiché la sintassi dei property wrapper è semplicemente zucchero sintattico per una proprietà con un getter e un setter, accedere a `height` e `width` si comporta come l'accesso a qualsiasi altra proprietà. Per esempio, il codice in `resize(to:)` accede a `height` e `width` usando il loro property wrapper. Se chiami `resize(to: .large)`, il caso dello switch per `.large` imposta l'altezza e la larghezza del rettangolo a 100. Il wrapper impedisce che il valore di quelle proprietà sia maggiore di 12, e imposta il valore proiettato a `true`, per registrare il fatto di averne corretto i valori. Alla fine di `resize(to:)`, l'istruzione return controlla `$height` e `$width` per determinare se il property wrapper ha corretto `height` o `width`.

## Variabili globali e locali <a href="#variabili-globali-e-locali" id="variabili-globali-e-locali"></a>

Le capacità descritte sopra per calcolare e osservare le proprietà sono disponibili anche per le *variabili globali* e le *variabili locali*. Le variabili globali sono variabili definite al di fuori di qualsiasi contesto di funzione, metodo, closure o tipo. Le variabili locali sono variabili definite all'interno di un contesto di funzione, metodo o closure.

Le variabili globali e locali che hai incontrato nei capitoli precedenti sono state tutte *variabili memorizzate*. Le variabili memorizzate, come le proprietà memorizzate, forniscono memoria per un valore di un certo tipo e permettono di impostare e recuperare quel valore.

Tuttavia, puoi anche definire *variabili calcolate* e definire osservatori per le variabili memorizzate, sia in un ambito globale sia in un ambito locale. Le variabili calcolate calcolano il loro valore, anziché memorizzarlo, e si scrivono nello stesso modo delle proprietà calcolate.

{% hint style="info" %}
**Nota:** Le costanti e le variabili globali vengono sempre calcolate in modo lazy, in modo simile a [Proprietà memorizzate lazy](/guida-al-linguaggio/proprieta.md#proprieta-memorizzate-lazy). A differenza delle proprietà memorizzate lazy, le costanti e le variabili globali non devono essere contrassegnate con il modificatore `lazy`.

Le costanti e le variabili locali non vengono mai calcolate in modo lazy.
{% endhint %}

Puoi applicare un property wrapper a una variabile memorizzata locale, ma non a una variabile globale o a una variabile calcolata. Per esempio, nel codice seguente, `myNumber` usa `SmallNumber` come property wrapper.

```swift
func someFunction() {
    @SmallNumber var myNumber: Int = 0

    myNumber = 10
    // ora myNumber vale 10

    myNumber = 24
    // ora myNumber vale 12
}
```

Come quando applichi `SmallNumber` a una proprietà, impostare il valore di `myNumber` a 10 è valido. Poiché il property wrapper non consente valori superiori a 12, imposta `myNumber` a 12 invece che a 24.

## Proprietà di tipo <a href="#proprieta-di-tipo" id="proprieta-di-tipo"></a>

Le proprietà di istanza sono proprietà che appartengono a un'istanza di un tipo particolare. Ogni volta che crei una nuova istanza di quel tipo, essa ha il proprio insieme di valori delle proprietà, separato da qualsiasi altra istanza.

Puoi anche definire proprietà che appartengono al tipo stesso, non a una singola istanza di quel tipo. Di queste proprietà esisterà sempre una sola copia, indipendentemente da quante istanze di quel tipo crei. Questo genere di proprietà è chiamato *proprietà di tipo*.

Le proprietà di tipo sono utili per definire valori universali per *tutte* le istanze di un tipo particolare, come una proprietà costante che tutte le istanze possono usare (come una costante statica in C), o una proprietà variabile che memorizza un valore globale per tutte le istanze di quel tipo (come una variabile statica in C).

Le proprietà di tipo memorizzate possono essere variabili o costanti. Le proprietà di tipo calcolate sono sempre dichiarate come proprietà variabili, nello stesso modo delle proprietà di istanza calcolate.

{% hint style="info" %}
**Nota:** A differenza delle proprietà di istanza memorizzate, devi sempre dare alle proprietà di tipo memorizzate un valore predefinito. Questo perché il tipo stesso non ha un inizializzatore che possa assegnare un valore a una proprietà di tipo memorizzata al momento dell'inizializzazione.

Le proprietà di tipo memorizzate vengono inizializzate in modo lazy al loro primo accesso. È garantito che vengano inizializzate una sola volta, anche quando vi accedono più thread contemporaneamente, e non devono essere contrassegnate con il modificatore `lazy`.
{% endhint %}

### Sintassi delle proprietà di tipo <a href="#sintassi-delle-proprieta-di-tipo" id="sintassi-delle-proprieta-di-tipo"></a>

In C e Objective-C, definisci costanti e variabili statiche associate a un tipo come variabili statiche *globali*. In Swift, invece, le proprietà di tipo si scrivono come parte della definizione del tipo, all'interno delle parentesi graffe esterne del tipo, e ogni proprietà di tipo ha un ambito esplicitamente limitato al tipo che supporta.

Definisci le proprietà di tipo con la parola chiave `static`. Per le proprietà di tipo calcolate dei tipi classe, puoi invece usare la parola chiave `class` per consentire alle sottoclassi di sovrascrivere l'implementazione della superclasse. L'esempio seguente mostra la sintassi per le proprietà di tipo memorizzate e calcolate:

```swift
struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}
enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 6
    }
}
class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}
```

{% hint style="info" %}
**Nota:** Gli esempi di proprietà di tipo calcolate qui sopra riguardano proprietà di tipo calcolate di sola lettura, ma puoi anche definire proprietà di tipo calcolate di lettura e scrittura con la stessa sintassi delle proprietà di istanza calcolate.
{% endhint %}

### Interrogare e impostare le proprietà di tipo <a href="#interrogare-e-impostare-le-proprieta-di-tipo" id="interrogare-e-impostare-le-proprieta-di-tipo"></a>

Le proprietà di tipo si interrogano e si impostano con la sintassi a punto, proprio come le proprietà di istanza. Tuttavia, le proprietà di tipo si interrogano e si impostano sul *tipo*, non su un'istanza di quel tipo. Per esempio:

```swift
print(SomeStructure.storedTypeProperty)
// Stampa "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// Stampa "Another value."
print(SomeEnumeration.computedTypeProperty)
// Stampa "6".
print(SomeClass.computedTypeProperty)
// Stampa "27".
```

Gli esempi che seguono usano due proprietà di tipo memorizzate come parte di una struttura che modella un indicatore di livello audio per un certo numero di canali audio. Ogni canale ha un livello audio intero compreso tra `0` e `10` inclusi.

La figura seguente illustra come due di questi canali audio possano essere combinati per modellare un indicatore di livello audio stereo. Quando il livello audio di un canale è `0`, nessuna delle luci di quel canale è accesa. Quando il livello audio è `10`, tutte le luci di quel canale sono accese. In questa figura, il canale sinistro ha un livello corrente di `9`, e il canale destro ha un livello corrente di `7`:

![](https://3581893755-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNzatREGwodw9Z93LQYJb%2Fuploads%2Fu9BARiSjjeHeoeFvDPEQ%2FstaticPropertiesVUMeter%402x.png?alt=media)

I canali audio descritti sopra sono rappresentati da istanze della struttura `AudioChannel`:

```swift
struct AudioChannel {
    static let thresholdLevel = 10
    static var maxInputLevelForAllChannels = 0
    var currentLevel: Int = 0 {
        didSet {
            if currentLevel > AudioChannel.thresholdLevel {
                // limita il nuovo livello audio al livello di soglia
                currentLevel = AudioChannel.thresholdLevel
            }
            if currentLevel > AudioChannel.maxInputLevelForAllChannels {
                // memorizza questo valore come nuovo livello di input massimo complessivo
                AudioChannel.maxInputLevelForAllChannels = currentLevel
            }
        }
    }
}
```

La struttura `AudioChannel` definisce due proprietà di tipo memorizzate per supportare la sua funzionalità. La prima, `thresholdLevel`, definisce il valore di soglia massimo che un livello audio può assumere. Si tratta di un valore costante di `10` per tutte le istanze di `AudioChannel`. Se arriva un segnale audio con un valore superiore a `10`, verrà limitato a questo valore di soglia (come descritto di seguito).

La seconda proprietà di tipo è una proprietà memorizzata variabile chiamata `maxInputLevelForAllChannels`. Questa tiene traccia del valore di input massimo ricevuto da *qualsiasi* istanza di `AudioChannel`. Parte con un valore iniziale di `0`.

La struttura `AudioChannel` definisce anche una proprietà di istanza memorizzata chiamata `currentLevel`, che rappresenta il livello audio corrente del canale su una scala da `0` a `10`.

La proprietà `currentLevel` ha un osservatore di proprietà `didSet` per controllare il valore di `currentLevel` ogni volta che viene impostato. Questo osservatore esegue due controlli:

* Se il nuovo valore di `currentLevel` è maggiore del `thresholdLevel` consentito, l'osservatore di proprietà limita `currentLevel` a `thresholdLevel`.
* Se il nuovo valore di `currentLevel` (dopo l'eventuale limitazione) è maggiore di qualsiasi valore ricevuto in precedenza da *qualsiasi* istanza di `AudioChannel`, l'osservatore di proprietà memorizza il nuovo valore di `currentLevel` nella proprietà di tipo `maxInputLevelForAllChannels`.

{% hint style="info" %}
**Nota:** Nel primo di questi due controlli, l'osservatore `didSet` imposta `currentLevel` a un valore diverso. Questo, tuttavia, non fa sì che l'osservatore venga chiamato di nuovo.
{% endhint %}

Puoi usare la struttura `AudioChannel` per creare due nuovi canali audio chiamati `leftChannel` e `rightChannel`, per rappresentare i livelli audio di un impianto audio stereo:

```swift
var leftChannel = AudioChannel()
var rightChannel = AudioChannel()
```

Se imposti il `currentLevel` del canale *sinistro* a `7`, puoi vedere che la proprietà di tipo `maxInputLevelForAllChannels` viene aggiornata e diventa uguale a `7`:

```swift
leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// Stampa "7".
print(AudioChannel.maxInputLevelForAllChannels)
// Stampa "7".
```

Se provi a impostare il `currentLevel` del canale *destro* a `11`, puoi vedere che la proprietà `currentLevel` del canale destro viene limitata al valore massimo di `10`, e la proprietà di tipo `maxInputLevelForAllChannels` viene aggiornata e diventa uguale a `10`:

```swift
rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// Stampa "10".
print(AudioChannel.maxInputLevelForAllChannels)
// Stampa "10".
```
