ProgrammazioneSviluppatore iOS

Che cos'è escaping e non-escaping closures in Swift? Come organizzare correttamente il lavoro con le chiusure, quali sono le sfide nel loro utilizzo e quali possono essere i rischi quando si lavora con codice asincrono?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Escaping closure è una chiusura che "esce" dal contesto della funzione chiamante (ad esempio, viene salvata per l'esecuzione asincrona).

Per impostazione predefinita in Swift, le funzioni accettano chiusure non-escaping: la chiusura viene eseguita all'interno della chiamata della funzione.

Per specificare esplicitamente escaping, si usa la parola chiave @escaping:

func asyncTask(completion: @escaping () -> Void) { DispatchQueue.main.async { completion() } }

Differenze chiave:

  • Le closure escaping possono essere memorizzate e chiamate in seguito.
  • Per catturare self in una closure escaping, è necessario specificare esplicitamente [weak self] o [unowned self] per prevenire perdite di memoria (retain cycle).

Domanda insidiosa.

È necessario scrivere sempre @escaping per le chiusure passate come parametri a una funzione che chiama DispatchQueue.async?

— Sì. Poiché DispatchQueue.async memorizza la chiusura fino al momento dell'esecuzione, la chiusura diventa escaping.

Esempio:

func foo(action: () -> Void) { DispatchQueue.main.async { action() // Non si compilerà: la chiusura deve essere escaping. } } // Necessario: func bar(action: @escaping () -> Void) { ... }

Esempi di errori reali a causa dell'ignoranza delle sfumature dell'argomento.


Storia

Il controller ha creato un riferimento forte a self all'interno di una closure escaping (ad esempio, una richiesta di rete). Il controller non veniva liberato dopo essere uscito dallo schermo — forte ciclo di retain. Soluzione: utilizzare [weak self] o [unowned self].


Storia

La funzione passava una closure a DispatchQueue.async, ma non la contrassegnava come escaping. Il progetto non si compilava, gli errori erano difficili da trovare a causa della nidificazione delle funzioni.


Storia

All'interno della closure si accedeva a un oggetto che era già deallocato al momento della chiamata della closure (si utilizzava [unowned self]). Di conseguenza — crash a runtime. Soluzione: utilizzare [weak self] e controllare nil.