func someFunctionThatTakesAClosure(closure: () -> ()) {
// function body goes here
}

// here’s how you call this function without using a trailing closure:

someFunctionThatTakesAClosure({
// closure’s body goes here
})

// here’s how you call this function with a trailing closure instead:

someFunctionThatTakesAClosure() {
// trailing closure’s body goes here
}

Le chiusure finali sono utili quando la chiusura è sufficientemente lunga da non essere possibile scriverla su una singola riga. Ad esempio, gli array in Swift hanno un metodo map che prende un’espressione di chiusura come unico argomento. La chiusura viene chiamata una volta per ogni elemento dell’array  e restituisce un valore map alternativo (anche di altro tipo) per quella voce. La natura della mappatura e il tipo del valore restituito è lasciata alla chiusura.

Dopo aver applicato la chiusura fornita a ciascun elemento della matrice, il metodo map restituisce un nuovo array contenente tutti i nuovi valori mappati, nello stesso ordine dei valori corrispondenti nell’array originale.

Ecco come possiamo utilizzare il metodo map con una chiusura finale per convertire una matrice di valori Int in un array di valori stringa. La matrice [16, 58, 510] viene utilizzata per creare il nuovo array [“OneSix”, “FiveEight”, “FiveOneZero”]:

let digitNames = [

0: “Zero”, 1: “One”, 2: “Two”, 3: “Three”, 4: “Four”,
5: “Five”, 6: “Six”, 7: “Seven”, 8: “Eight”, 9: “Nine”
]
let numbers = [16, 58, 510]

Il codice appena visto crea un dizionario di mapping tra le cifre intere e le versioni in lingua inglese dei loro nomi. Esso definisce anche una serie di numeri interi, pronti per essere convertito in stringhe.

È ora possibile utilizzare la matrice per creare un array di valori String, passando un’espressione di chiusura alla mappa il metodo della matrice come una chiusura finale.

let strings = numbers.map {
(var number) -> String in
var output = “”
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
// strings is inferred to be of type String[]
// its value is [“OneSix”, “FiveEight”, “FiveOneZero”]

La funzione map richiama l’espressione di chiusura una volta per ogni elemento dell’array. Non è necessario specificare il tipo di parametro di ingresso della chiusura, perché questo può essere dedotto dai valori nella matrice da mappare.

In questo esempio, il parametro numero di chiusura è definito come variabile, in modo che il valore possa essere modificato all’interno del corpo di chiusura, piuttosto che dichiarare una nuova variabile e assegnando il valore numerico passato ad esso. L’espressione di chiusura specifica anche un tipo di ritorno  String, per indicare il tipo che verrà memorizzato nella matrice di uscita.

L’espressione di chiusura costruisce una stringa chiamata in uscita ogni volta che viene eseguita. Calcola l’ultima cifra del numero utilizzando l’operatore resto (numero% 10), e usa questa cifra per cercare una stringa appropriata nelle digitNames.

La stringa recuperata dal dizionario digitNames viene aggiunta, costruendo efficacemente una versione stringa del numero, procedendo al contrario. (Il numero espressione% 10 dà un valore di 6 per 16, 8 per 58, e 0 per 510.)

La variabile numero viene poi divisa per 10. Poiché si tratta di un numero intero, viene arrotondato per difetto durante la divisione, quindi 16 diventa 1, 58 diventa 5, e 510 diventa 51.

Il processo viene ripetuto fino a quando il numero / = 10 è uguale a 0, il punto in cui la stringa di output viene restituita dalla chiusura, e viene aggiunto alla matrice di uscita dalla funzione mappa.
Catturare valori
Una chiusura può catturare costanti e variabili dal contesto circostante in cui è definita. La chiusura può consultare e modificare i valori di tali costanti e variabili, anche se la portata originale che definisce le costanti e variabili non esiste più.

La forma più semplice di una chiusura in Swift è una funzione nidificata, scritta nel corpo di un’altra funzione. Una funzione annidata può catturare uno qualsiasi degli argomenti della sua funzione esterna e può anche catturare eventuali costanti e variabili definite all’interno della funzione esterna.

Ecco un esempio di una funzione chiamata makeIncrementor, che contiene una funzione annidata chiamata incrementator. La funzione incrementator annidata cattura due valori, RunningTotal e quantità, dal suo contesto circostante. Dopo la cattura di questi valori, incrementator viene restituito da makeIncrementor come una chiusura che aumenta RunningTotal ogni volta che viene chiamato.

func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}

Il tipo di ritorno di makeIncrementor è () -> Int. Ciò significa che restituisce una funzione, invece di un valore semplice. La funzione return non ha parametri, e restituisce un valore Int ogni volta che viene chiamato.

La funzione makeIncrementor definisce una variabile intera chiamata RunningTotal,  inizializzata con il valore 0.

La funzione makeIncrementor ha un solo parametro Int con un nome esterno di forIncrement, e un nome locale di import. Il valore argomento passato a questo parametro specifica di quanto RunningTotal dovrebbe essere incrementato di volta in volta che la funzione viene chiamata.

makeIncrementor definisce una funzione annidata chiamata incrementator, che esegue l’effettiva azione di incremento. Questa funzione aggiunge semplicemente “importo ” a RunningTotal, e restituisce il risultato.

Se considerati separatamente, la funzione incrementator nidificata potrebbe sembrare insolita:

func incrementor() -> Int {
runningTotal += amount
return runningTotal
}

La funzione incrementator non ha parametri, ma si riferisce ad RunningTotal e importo dall’interno suo corpo della funzione. Lo fa catturando i valori attuali di RunningTotal e quantità dalla sua funzione circostante e il loro utilizzo nel proprio corpo della funzione.

Poiché non modifica quantità, incrementator effettivamente cattura e memorizza una copia del valore memorizzato in quantità. Questo valore viene memorizzato insieme alla nuova funzione incrementator.

Tuttavia, poichè modifica la variabile RunningTotal ogni volta che viene chiamato, incrementator cattura un riferimento alla variabile RunningTotal corrente e non solo una copia del suo valore iniziale. Catturare un riferimento assicura che RunningTotal non scompaia quando la chiamata a makeIncrementor finisce, e assicura che RunningTotal continuerà ad essere disponibile la prossima volta che la funzione incrementator verrà chiamata.

Ecco un esempio di makeIncrementor in azione:

Chiamando la funzione più volte mostra questo comportamento in azione:

incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30

Se si crea un altro incrementator, avrà un proprio riferimento memorizzato in una nuova variabile RunningTotal separato. Nell’esempio che segue, incrementBySeven cattura un riferimento ad una nuova variabile RunningTotal non collegato a quello catturato da incrementByTen:

let incrementBySeven = makeIncrementor(forIncrement: 7)
incrementBySeven()
// returns a value of 7
incrementByTen()
// returns a value of 40

Chiusure sono tipi di riferimento

Ogni volta che si assegna una funzione o una chiusura di una costante o una variabile, in realtà si sta specificando che la costante o la variabile sia un riferimento alla funzione o la chiusura. Nell’esempio precedente, è la scelta di chiusura che incrementByTen riferisce a quella è costante, e non al contenuto della chiusura stessa.

Questo significa anche che se si assegna una chiusura di due costanti o variabili diverse, entrambe le costanti o variabili si riferiscono alla stessa chiusura:

let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// returns a value of 50

 

VOLETE ESSERE AGGIORNATI TEMPESTIVAMENTE ALL’USCITA DI UN NUOVO CAPITOLO ? BASTA UN LIKE SULLA NOSTRA PAGINA FACEBOOK!