Rapido parametro di chiusura di fuga opzionale


162

Dato:

typealias Action = () -> ()

var action: Action = { }

func doStuff(stuff: String, completion: @escaping Action) {
    print(stuff)
    action = completion
    completion()
}

func doStuffAgain() {
    print("again")
    action()
}

doStuff(stuff: "do stuff") { 
    print("swift 3!")
}

doStuffAgain()

C'è un modo per rendere il completionparametro (e action) di tipo Action?e anche mantenere @escaping?

La modifica del tipo genera il seguente errore:

L'attributo @escaping si applica solo ai tipi di funzione

Rimuovendo l' @escapingattributo, il codice viene compilato ed eseguito, ma non sembra essere corretto poiché la completionchiusura sta sfuggendo all'ambito della funzione.


21
"Rimuovendo l' @escapingattributo, il codice viene compilato ed eseguito" - Questo perché, come descritto in SR-2444 , Action?è, per impostazione predefinita, in escape. Pertanto, rimuovere @escapingquando si utilizza la chiusura opzionale consente di ottenere ciò di cui hai bisogno.
Rob,


le chiusure di tipo alias stanno scappando
Masih

Ecco un eccellente articolo di Ole Begemann che descrive perché sta accadendo e alcune soluzioni alternative se si desidera che i parametri opzionali siano @noescape.
Sensuale

Risposte:


122

Esiste un report SR-2552 che @escapingnon riconosce l'alias del tipo di funzione. ecco perché l'errore @escaping attribute only applies to function types. puoi risolvere il problema espandendo il tipo di funzione nella firma della funzione:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: (@escaping ()->())?) {
    print(stuff)
    action = completion
    completion?()
}

func doStuffAgain() {
    print("again")
    action?()
}

doStuff(stuff: "do stuff") {
    print("swift 3!")
}

doStuffAgain()

EDIT 1 ::

In realtà ero in una versione beta di xcode 8 in cui il bug SR-2552 non era ancora stato risolto. risolto quel bug, ne ho introdotto uno nuovo (quello che stai affrontando) che è ancora aperto. vedi SR-2444 .

La soluzione alternativa @Michael Ilseman indicata come soluzione temporanea è rimuovere l' @escapingattributo dal tipo di funzione opzionale, che mantiene la funzione come escape .

func doStuff(stuff: String, completion: Action?) {...}

EDIT 2 ::

L' SR-2444 è stato chiuso affermando esplicitamente che le chiusure nelle posizioni dei parametri non stanno sfuggendo e hanno bisogno che siano contrassegnate con @escapingper renderle in fuga, ma i parametri opzionali sono implicito in fuga, poiché ((Int)->())?è un sinonimo di Optional<(Int)->()>, chiusure opzionali stanno scappando.


5
Ora @escaping may only be applied to parameters of function type func doStuff(stuff: String, completion: (@escaping ()->())?) {
ricevo

1
a temporary solution is remove the @escaping attribute from optional function type, that keep the function as escaping. Puoi spiegarlo ulteriormente? La semantica predefinita in swift 3 non è escape. Sebbene si compili senza @escaping, temo che causerà problemi essendo trattato come non escape. Non è vero?
Pat Niemeyer,

49
Dopo ulteriori letture vedo che SR-2444 dice che tutte le chiusure opzionali vengono trattate come escape, il che è un bug complementare :) Presumo che quando sarà risolto la compilazione ci avvertirà di apportare la modifica.
Pat Niemeyer,

Forse un po 'fuori tema; ma come funziona @autoclosure? Si ottiene lo stesso errore lì ...
Gee.E

funziona senza @escaping __ func doStuff (stuff: String, completamento: (() -> ())?) {
Феннур Мезитов

226

da: mailing list di utenti veloci

Fondamentalmente, @escaping è valido solo per le chiusure nella posizione del parametro della funzione. La regola noescape per impostazione predefinita si applica solo a queste chiusure nella posizione del parametro della funzione, altrimenti stanno scappando. Gli aggregati, come gli enum con valori associati (es. Opzionale), le tuple, le strutture, ecc., Se hanno chiusure, seguono le regole di default per chiusure che non si trovano nella posizione del parametro della funzione, cioè che stanno scappando.

Quindi il parametro funzione opzionale è @escaping per impostazione predefinita.
@noeascape si applica solo al parametro della funzione per impostazione predefinita.


7
Penso che questo aggiunga le informazioni più importanti all'argomento, dovrebbe essere accettata la risposta.
Damian Dudycz,

La risposta motivata Quanto è probabile che ciò possa cambiare?
GoldenJoe,

2
Questo ha senso dal momento che tecnicamente dire (()->Void)?è lo stesso che dire che hai Optional<()->Void>e per Optionalmantenere la proprietà dovrebbe solo accettare le @escapingfunzioni. Terzo che questa dovrebbe essere la risposta accettata. Grazie.
Dean Kelly,

22

Mi sono imbattuto in un problema simile perché mescolare @escapinge non @escapingè molto confuso, soprattutto se è necessario passare le chiusure.

Ho finito per assegnare un valore predefinito no-op al parametro di chiusura tramite = { _ in }, che penso abbia più senso:

func doStuff(stuff: String = "do stuff",
        completion: @escaping (_ some: String) -> Void = { _ in }) {
     completion(stuff)
}

doStuff(stuff: "bla") {
    stuff in
    print(stuff)
}

doStuff() {
    stuff in
    print(stuff)
}

Questo è pulito e piuttosto in fase di implementazione.
Fred Faust,

2
Cosa succede se desidero verificare se questo blocco è nullo / vuoto?
Vyachaslav Gerchicov

2
Purtroppo, questo non funziona per un metodo di protocollo. "Argomento predefinito non consentito in un metodo di protocollo" (Xcode 8.3.2).
Mike Taverne,

17

L'ho fatto funzionare in Swift 3 senza alcun avvertimento solo in questo modo:

func doStuff(stuff: String, completion: (()->())? ) {
    print(stuff)
    action = completion
    completion?()
}

4

La cosa importante da capire nell'esempio è che se passi Actionalla Action?chiusura sta scappando. Quindi, facciamo quello che proponi:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: Action?) {
    print(stuff)
    action = completion
    completion?()
}

Bene, ora chiameremo doStuff:

class ViewController: UIViewController {
    var prop = ""
    override func viewDidLoad() {
        super.viewDidLoad()
        doStuff(stuff: "do stuff") {
            print("swift 3!")
            print(prop) // error: Reference to property 'prop' in closure 
                        // requires explicit 'self.' to make capture semantics explicit
        }
    }
}

Bene, questo requisito sorge solo per la fuga dalle chiusure. Quindi la chiusura sta scappando. Ecco perché non lo contrassegni come in fuga: sta già scappando.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.