Chiusure di fuga in Swift

Sono nuovo di Swift e stavo leggendo il manuale quando mi sono imbattuto in chiusure in fuga. Non ho avuto la descrizione del manuale. Qualcuno potrebbe spiegarmi come sono le chiusure che scappano in Swift in termini semplici.

Considera questa class:

 class A { var closure: (() -> Void)? func someMethod(closure: () -> Void) { self.closure = closure } } 

someMethod assegna la chiusura passata a una proprietà nella class.

Ora ecco che arriva un’altra class:

 class B { var number = 0 var a: A = A() func anotherMethod() { a.someMethod { self.number = 10 } } } 

Se chiamo anotherMethod , la chiusura { self.number = 10 } sarà memorizzata nell’istanza di A Dal momento che il self viene catturato nella chiusura, anche l’istanza di A avrà un forte riferimento ad essa.

Questo è fondamentalmente un esempio di chiusura fuggita!

Probabilmente ti starai chiedendo, “che cosa? Quindi da dove è stata fuggita la chiusura e?”

La chiusura sfugge dall’ambito del metodo, allo scopo della class. E può essere chiamato più tardi, anche su un altro thread! Questo potrebbe causare problemi se non gestito correttamente.

Per evitare di sfuggire involontariamente alle chiusure e causare cicli di ritenzione e altri problemi, utilizzare l’attributo @noescape :

 class A { var closure: (() -> Void)? func someMethod(@noescape closure: () -> Void) { } } 

Ora se provi a scrivere self.closure = closure , non viene compilato!

Aggiornare:

In Swift 3, tutti i parametri di chiusura non possono uscire per impostazione predefinita. È necessario aggiungere l’attributo @escaping per rendere la chiusura in grado di uscire dall’ambito corrente. Questo aggiunge molta più sicurezza al tuo codice!

 class A { var closure: (() -> Void)? func someMethod(closure: @escaping () -> Void) { } } 

Sto andando in un modo più semplice.

Considera questo esempio:

 func testFunctionWithNonescapingClosure(closure:() -> Void) { closure() } 

Quanto sopra è una chiusura senza escape perché la chiusura è invocata prima che il metodo ritorni.

Considera lo stesso esempio con un’operazione asincrona:

 func testFunctionWithEscapingClosure(closure:@escaping () -> Void) { DispatchQueue.main.async { closure() } } 

L’esempio precedente contiene una chiusura di escape poiché l’invocazione di chiusura può avvenire dopo che la funzione è tornata a causa dell’operazione asincrona.

  var completionHandlers: [() -> Void] = [] func testFunctionWithEscapingClosure(closure: @escaping () -> Void) { completionHandlers.append(closure) } 

Nel caso precedente puoi facilmente capire che la chiusura si sta spostando all’esterno del corpo della funzione, quindi deve essere una chiusura di fuga.

La chiusura di escape e non di escape è stata aggiunta per l’ottimizzazione del compilatore in Swift 3. È ansible cercare i vantaggi della chiusura senza nonescaping .

Trovo che questo sito web sia molto utile a tale riguardo. Una spiegazione semplice sarebbe:

Se una chiusura viene passata come argomento a una funzione e viene invocata dopo che la funzione è tornata, la chiusura sta eseguendo l’escape.

Leggi di più sul link che ho passato sopra! 🙂

Swift 4.1

Dal riferimento del linguaggio: attributi di The Swift Programming Language (Swift 4.1)

Apple spiega chiaramente come l’attributo escaping .

Applicare questo attributo al tipo di un parametro in una dichiarazione di metodo o funzione per indicare che il valore del parametro può essere memorizzato per l’esecuzione successiva. Ciò significa che il valore può superare la durata della chiamata. I parametri del tipo di funzione con l’attributo del tipo di escape richiedono l’uso esplicito di self. per proprietà o metodi. Per un esempio su come utilizzare l’attributo di escape, vedere Escaping Closures

 var completionHandlers: [() -> Void] = [] func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) { completionHandlers.append(completionHandler) } 

La funzione someFunctionWithEscapingClosure(_:) accetta una chiusura come argomento e la aggiunge a un array dichiarato all’esterno della funzione. Se non si contrassegnasse il parametro di questa funzione con @escaping , si otterrebbe un errore in fase di compilazione.

Si dice che una chiusura sfugge ad una funzione quando la chiusura viene passata come argomento alla funzione, ma viene richiamata dopo il ritorno della funzione. Quando dichiari una funzione che accetta una chiusura come uno dei suoi parametri, puoi scrivere @escaping prima del tipo del parametro per indicare che la chiusura può uscire.