Schneller optionaler Escape-Schließparameter

162

Gegeben:

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()

Gibt es eine Möglichkeit, den completionParameter (und action) vom Typ zu machen Action?und auch zu behalten @escaping?

Das Ändern des Typs führt zu folgendem Fehler:

Das Attribut @escaping gilt nur für Funktionstypen

Wenn Sie das @escapingAttribut entfernen , wird der Code kompiliert und ausgeführt, scheint jedoch nicht korrekt zu sein, da der completionAbschluss dem Funktionsumfang entgeht.

Lescai Ionel
quelle
21
"Entfernen des @escapingAttributs, Kompilieren und Ausführen des Codes" - Dies liegt daran, dass, wie in SR-2444 beschrieben , Action?standardmäßig ein Escapezeichen angezeigt wird. So, das Entfernen , @escapingwenn der optionalen Verschluss mit vollbringt , was Sie brauchen.
Rob
Typ Alias ​​Schließungen entkommen
Masih
Hier ist ein ausgezeichneter Artikel von Ole Begemann , der beschreibt, warum dies geschieht, und einige Problemumgehungen, wenn optionale Parameter @noescape sein sollen.
Sinnvoll

Antworten:

122

Es gibt einen SR-2552- Bericht, der @escapingkeinen Funktionstyp-Alias ​​erkennt. deshalb der fehler @escaping attribute only applies to function types. Sie können das Problem umgehen, indem Sie den Funktionstyp in der Funktionssignatur erweitern:

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: :

Ich war tatsächlich unter einer Beta-Version von xcode 8, in der der Fehler SR-2552 noch nicht behoben wurde. Um diesen Fehler zu beheben, wurde ein neuer Fehler eingeführt (der, mit dem Sie konfrontiert sind), der noch offen ist. siehe SR-2444 .

Die Problemumgehung, die @Michael Ilseman als vorübergehende Lösung bezeichnet hat, besteht darin, das @escapingAttribut aus dem optionalen Funktionstyp zu entfernen, wodurch die Funktion als maskiert bleibt .

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

EDIT 2: :

Der SR-2444 wurde geschlossen und weist ausdrücklich darauf hin, dass Schließungen in Parameterpositionen nicht ausgeblendet werden und mit gekennzeichnet werden müssen @escaping, damit sie ausgeblendet werden. Die optionalen Parameter werden jedoch implizit ausgeblendet, da dies ((Int)->())?ein Synonym für Optional<(Int)->()>optionale Verschlüsse ist.

Jans
quelle
5
Jetzt bekommen@escaping may only be applied to parameters of function type func doStuff(stuff: String, completion: (@escaping ()->())?) {
Lescai Ionel
1
a temporary solution is remove the @escaping attribute from optional function type, that keep the function as escaping. Können Sie das weiter erklären? Die Standardsemantik in Swift 3 ist nicht entkommen. Obwohl es ohne @escaping kompiliert wird, befürchte ich, dass es Probleme verursachen wird, wenn es als nicht entkommen behandelt wird. Ist das nicht wahr?
Pat Niemeyer
49
Beim weiteren Lesen sehe ich, dass SR-2444 sagt, dass alle optionalen Schließungen als Escape behandelt werden, was ein komplementärer Fehler ist :) Ich gehe davon aus, dass die Kompilierung uns warnt, die Änderung vorzunehmen, wenn sie behoben ist.
Pat Niemeyer
Vielleicht ein wenig abseits des Themas; aber wie funktioniert das @autoclosure? Man bekommt dort den gleichen Fehler ...
Gee.E
es funktioniert ohne @escaping __ func doStuff (Zeug: String, Vervollständigung: (() -> ())?) {
Феннур Мезитов
226

von: Mailingliste für schnelle Benutzer

Grundsätzlich gilt @escaping nur für Schließungen in der Position der Funktionsparameter. Die Noescape-Standardregel gilt nur für diese Verschlüsse an der Position der Funktionsparameter, andernfalls werden sie ausgeblendet. Aggregate wie Aufzählungen mit zugehörigen Werten (z. B. optional), Tupel, Strukturen usw. folgen, wenn sie Abschlüsse haben, den Standardregeln für Abschlüsse, die sich nicht an der Position der Funktionsparameter befinden, dh sie werden maskiert.

Der optionale Funktionsparameter ist also standardmäßig @escaping.
@noeascape gilt standardmäßig nur für Funktionsparameter.

Dmitry Coolerov
quelle
7
Ich denke, dies fügt dem Thema die wichtigsten Informationen hinzu, es sollte als Antwort akzeptiert werden.
Damian Dudycz
Die begründete Antwort. Wie wahrscheinlich ist es, dass sich dies ändern könnte?
GoldenJoe
2
Dies ist sinnvoll, da das technische Sprichwort (()->Void)?das gleiche ist wie das Sprichwort, das Sie haben, Optional<()->Void>und um das OptionalEigentum aufrechtzuerhalten, müssten nur @escapingFunktionen akzeptiert werden. Ich werde drittens sagen, dass dies die akzeptierte Antwort sein sollte. Danke dir.
Dean Kelly
21

Ich bin auf ein ähnliches Problem @escapinggestoßen, weil das Mischen und Nicht- Mischen @escapingsehr verwirrend ist, besonders wenn Sie die Verschlüsse herumreichen müssen.

Am Ende habe ich dem Closure-Parameter über einen No-Op-Standardwert zugewiesen = { _ in }, was meiner Meinung nach sinnvoller ist:

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)
}
Freeman Man
quelle
Dies ist sauber und hübsch in der Implementierung.
Fred Faust
2
Was ist, wenn ich überprüfen möchte, ob dieser Block null / leer ist?
Vyachaslav Gerchicov
2
Schade, das funktioniert bei einer Protokollmethode nicht. "Standardargument in einer Protokollmethode nicht zulässig" (Xcode 8.3.2).
Mike Taverne
17

Ich habe es in Swift 3 ohne Warnungen nur so zum Laufen gebracht:

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

Das Wichtigste, was Sie im Beispiel verstehen sollten, ist, dass beim Wechsel Actionzum Action?Verschluss ein Escapezeichen entsteht. Machen wir also, was Sie vorschlagen:

typealias Action = () -> ()

var action: Action? = { }

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

Okay, jetzt rufen wir an 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
        }
    }
}

Nun, diese Anforderung entsteht nur, um Verschlüssen zu entkommen. Die Schließung entkommt also. Deshalb markiert man es nicht als entkommend - es entkommt bereits.

matt
quelle