Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Closure

Il linguaggio Swift supporta uno dei costrutti tipici della programmazione funzionale, vale a dire le closure: ecco cosa sono e come utilizzarle.
Il linguaggio Swift supporta uno dei costrutti tipici della programmazione funzionale, vale a dire le closure: ecco cosa sono e come utilizzarle.
Link copiato negli appunti

Negli articoli precedenti abbiamo visto come le funzioni costituiscono un costrutto essenziale in Swift per suddividere il codice in blocchi riutilizzabili. Oltre ad esse, Swift offre le cosiddette closure. Una closure è di fatto una funzione senza nome, che è possibile assegnare ad una variabile o ad una costante, passare come parametro o restituire come se fosse un valore.

Vediamone subito un esempio:

var add = {(a: Int, b: Int) -> Int in
    return a+b
}

Una closure è racchiusa da una coppia di parentesi graffe. Al suo interno, in maniera simile ad una funzione, troviamo la definizione dei parametri con il tipo del valore di ritorno. A differenza delle funzioni, il corpo della closure viene specificato dopo la parola riservata in.

Nell'esempio precedente, la closure è stata assegnata ad una variabile di nome add. Come abbiamo ripetuto in precedenza, Swift è un linguaggio fortemente tipizzato, così anche add avrà un tipo ben determinato:

var add: (Int, Int) -> Int

In questo caso, esso è assegnato dal compilatore tramite type inference, ma potevamo assegnarlo anche in maniera esplicita.

È possibile adesso invocare la closure tramite la variabile che la contiene, come se fosse una funzione:

add(5,8)

Grazie al meccanismo di type inference, possiamo semplificare la definizione della closure eliminando il tipo di ritorno:

var add2 = {(a:Int, b: Int) in
    return a+b
}

Se definiamo anticipatamente il tipo della variabile che conterrà la closure, possiamo omettere anche il tipo dei parametri:

var add3: (Int,Int) -> Int
add3 = {(a,b) in
    return a+b
}

Possiamo eliminare inoltre il return finale. Se l'ultima riga è un'espressione, Swift ritorna implicitamente il suo valore:

var add4: (Int,Int) -> Int
add4 = {(a,b) in
    a+b
}

Infine, è possibile anche rimuovere la lista dei parametri e la parola riservata in, usando semplicemente la loro posizione:

var add5: (Int,Int) -> Int
add5 = { $0 + $1 }

Come si può notare, le closure sono uno strumento utile per definire funzioni in modo sintetico, o per scrivere codice al volo (o, come si dice, inline), ad esempio nel corpo di altre funzioni. Per chi conosce Objective-C, le closure sono l'analogo dei blocchi.

Consideriamo ad esempio una funzione generica che accetti due valori di input, e che esegua un'operazione su questi:

func calculate(a: Int, _ b: Int, op: (Int, Int) -> Int) -> Int {
    let res = op(a, b)
    return res
}

Possiamo invocare la funzione appena definita passando una delle closure viste prima:

calculate(5, 8, op: add5)

In alternativa, possiamo definire una nuova closure inline, che ad esempio effettua la moltiplicazione degli operandi:

calculate(3, 2, op: {$0 * $1})

In questo caso, se la clousure è l'ultimo paramentro della funzione, è possibile semplificare ulteriormente la sua sintassi (utilizzando la cosiddetta trailing closure syntax):

calculate(5, 3) { $0 - $1 }

Le clousure hanno un'ulteriore caratteristica rispetto alla normali funzioni, quella di catturare al proprio interno variabili o altri oggetti appartenenti al contesto (o scope) in cui sono definite.

Facciamo un esempio, definendo una funzione creaContatore per generare dei contatori:

func creaContatore() -> () -> Int {
    var c = 0
    let incrementer = {
        return c++
    }
    return incrementer
}

La funzione creaContatore() restituisce una nuova closure, capace di incrementare un proprio contatore:

var contatore = creaContatore()
contatore()     // 0
contatore()     // 1
contatore()     // 2
var contatore2 = creaContatore()
contatore2()        // 0
contatore2()        // 1

In creaContatore(), la variabile c è stata catturata all'interno della closure incrementer: anche dopo l'invocazione della funzione creaContatore(), la variabile c non viene quindi deallocata, ma rimane persistentemente all'interno dello scope della closure. Ciò può risultare strano, poiché normalmente tutte le variabili dello scope di una funzione vengono automaticamente deallocate al termine della sua esecuzione.

Ti consigliamo anche