ProgrammazioneSviluppatore iOS

Spiega il funzionamento delle closures in Swift: differenze rispetto alle funzioni, peculiarità del catturamento delle variabili, possibili problemi di un utilizzo scorretto.

Supera i colloqui con l'assistente IA Hintsage

Risposta

Le closures in Swift sono blocchi di codice autonomi che possono catturare e mantenere riferimenti a variabili e costanti dal contesto circostante. Permettono di organizzare callback, elaborazioni asincrone e di memorizzare l'esecuzione del codice come valori delle variabili.

Differenze rispetto alle funzioni

  • Le closures possono catturare variabili dall'ambito esterno.
  • Hanno una sintassi più compatta.
  • Possono essere passate come valori.

Esempio di closure:

var counter = 0 let incrementer: () -> Void = { counter += 1 } incrementer() print(counter) // 1

Peculiarità del catturamento

  • Per impostazione predefinita, le variabili vengono catturate strong (per riferimento forte).
  • È possibile definire esplicitamente le regole di catturamento attraverso la capture list ([weak self], [unowned self]).

Problemi

  • Il problema principale è il potenziale retain cycle quando si cattura self all'interno della closure.
  • Nel codice multithread, è possibile catturare valori obsoleti delle variabili inconsapevolmente.

Domanda trabocchetto

Qual è la differenza tra strong-capture e l'uso di [weak self] o [unowned self] nella capture list della closure? Quali sono le conseguenze?

Risposta: Se non si specifica [weak self] o [unowned self], la closure catturerà self per riferimento forte per impostazione predefinita, il che porterà a un retain cycle se la closure e self si riferiscono l'uno all'altro. [weak self] cattura self per riferimento debole (opzionale), [unowned self] per riferimento non controllato (non opzionale). Una scelta errata o l'assenza della capture list porta a perdite di memoria o crash.

class Test { var closure: (() -> Void)? func setup() { closure = { [weak self] in self?.doWork() } } func doWork() { ... } }

Esempi di errori reali a causa della mancanza di conoscenza delle sfumature del tema


Storia

Un programmatore non ha utilizzato la capture list in una closure all'interno di UIViewController, che avviava un caricamento dati asincrono. Di conseguenza, il controller e la sua view non venivano liberati, causando una memory leak dopo la chiusura (dismiss). L'analisi dell'infrastruttura ha mostrato centinaia di controllori "bloccati" in memoria.


Storia

La closure ha catturato self utilizzando [unowned self], ma il ciclo di vita di self è terminato prima della closure. Si è verificato un crash quando si tentava di accedere a un oggetto già liberato — l'app si chiudeva quando si cercava di lavorare con self all'interno della closure.


Storia

Nel progetto, all'interno di una closure annidata, sono state catturate due variabili: una per riferimento forte, l'altra per riferimento debole. È risultato che il riferimento weak si annullava troppo presto, e i dati necessari per il funzionamento della closure erano stati persi — il bug si è manifestato solo nel stress-test sotto carico in un contesto multithreading.