wake-up-neo.net

Wenn Sie einen erzwungenen Downcast als optional behandeln, wird dies niemals "Null" sein.

Ich habe mit Swift herumgespielt und festgestellt, dass ich beim Abwärtswerfen eines Objekts, das in ein Wörterbuch eingefügt werden soll, eine seltsame Warnung erhält: Treating a forced downcast to 'String' as optional will never produce 'nil'. Wenn ich as durch as? ersetze, verschwindet die Warnung.

func test() -> AnyObject! {
  return "Hi!"
}

var dict = Dictionary<String,String>()
dict["test"]=test() as String

Apples Dokumentation sagt Folgendes aus

Da das Downcasting fehlschlagen kann, gibt es zwei verschiedene Formen für den Typumwandlungsoperator. Das optionale Formular as? gibt einen optionalen Wert des Typs zurück, zu dem Sie einen Downcast ausführen möchten. Das erzwungene Formular as versucht den Downcast und erzwingt das Ergebnis als eine einzelne zusammengesetzte Aktion.

Es ist mir unklar, warum die Verwendung von as? anstelle von as hier richtig ist. Einige Tests zeigen, dass der Code mit einem Fehler beendet wird, wenn ich test () wechsle, um ein Int statt eines Strings zurückzugeben, wenn ich weiterhin as benutze. Wenn ich zur Verwendung von as? wechsle, wird der Code normal ausgeführt und diese Anweisung übersprungen (dict bleibt leer). Ich bin mir jedoch nicht sicher, warum dies vorzuziehen ist. Meiner Meinung nach würde ich das Programm lieber mit einem Fehler beenden und mich wissen lassen, dass die Besetzung nicht erfolgreich war, dann einfach die fehlerhafte Aussage ignorieren und die Ausführung fortsetzen.

Laut Dokumentation sollte ich das erzwungene Formular verwenden "nur wenn Sie sicher sind, dass der Downcast immer erfolgreich sein wird". In diesem Fall bin ich sicher, dass der Downcast immer erfolgreich sein wird, da ich weiß, dass test() nur einen String zurückgeben kann. Ich würde also davon ausgehen, dass dies die perfekte Situation für die erzwungene Form des Downcastings ist. Warum gibt der Compiler eine Warnung aus?

29
Pamelloes

Schauen wir uns Ihre letzte Zeile genauer an und explodieren Sie sie, um zu sehen, was passiert:

let temporaryAnyObject = test()
let temporaryString = temporaryAnyObject as String
dict["test"] = temporaryString

Der Fehler befindet sich in der zweiten Zeile, in der Sie dem Compiler mitteilen, dass temporaryAnyObject definitiv eine String ist. Der Code wird immer noch kompiliert (vorausgesetzt, Sie behandeln Warnungen nicht als Fehler), stürzt jedoch ab, wenn temporaryAnyObject nicht tatsächlich eine String ist. 

Die Art und Weise, wie as ohne ? funktioniert, besagt im Wesentlichen "dass das Ergebnis dieses Ausdrucks von nun an als dieser Typ behandelt wird, wenn das Ergebnis tatsächlich von diesem Typ ist. Andernfalls sind wir inkonsistent geworden und können nicht mehr ausgeführt werden.

Die Art und Weise, wie as? (mit dem ?) arbeitet, besagt "von nun an das Ergebnis dieses Ausdrucks als diesen Typ behandeln, wenn das Ergebnis tatsächlich von diesem Typ ist, andernfalls ist das Ergebnis dieses Ausdrucks nil.

Wenn also in meinem obigen Explosionsbeispiel test() eine String zurückgibt, ist der as-Downcast erfolgreich und temporaryString ist jetzt eine String. Wenn test() keine String zurückgibt, sondern eine Int oder etwas anderes, das nicht von String abgeleitet ist, schlägt die as fehl und der Code kann nicht mehr ausgeführt werden. 

Dies liegt daran, dass Sie als Entwickler, der die vollständige Kontrolle hat, dem System befohlen haben, sich so zu verhalten, indem Sie das optionale Kennzeichen ? nicht setzen. Der Befehl as bedeutet insbesondere, dass Sie optionales Verhalten nicht tolerieren und dass der Downcast funktionieren muss.

Wenn Sie den ? eingegeben hätten, wäre temporaryStringnil und die dritte Zeile würde einfach das "test" -Schlüssel/Wert-Paar aus dem Wörterbuch entfernen.

Dies mag seltsam erscheinen, aber dies ist nur deshalb der Fall, weil dies das entgegengesetzte Standardverhalten vieler Sprachen ist, wie z. B. Obj-C, die alles standardmäßig als optional behandeln und sich darauf verlassen, dass Sie Ihre eigenen Prüfungen und Zusicherungen vornehmen.

Bearbeiten - Swift 2 Update

Seit Swift 2 wurde der erzwungene, fehlgeschlagene Downcast-Operator as entfernt und durch as! ersetzt. Dies ist viel schneller. Das Verhalten ist das gleiche.

43
Ryan

Sie können diese Warnung aus zwei Blickwinkeln lösen. 1. Der Wert, den Sie zurückgeben. 2. Der Typ, den Sie erwartet erwarten. Die andere Antwort spricht vom ersten Winkel. Ich spreche vom zweiten Winkel

Dies liegt daran, dass Sie einen erzwungenen und umgossenen-Wert für einen optional-Wert zurückgeben. Der Compiler lautet wie folgt: "Wenn Sie wirklich alle Optionals umsetzen wollen, dann machen Sie den erwarteten Rückgabeparameter nicht einfach zu einem nicht optional"

Zum Beispiel, wenn Sie geschrieben haben

func returnSomething<T> -> T?{ // I'm an optional, I can handle nils SAFELY and won't crash.

return UIViewController as! T // will never return a safe nil, will just CRASH

}

Im Grunde haben Sie sich selbst (und dem Compiler) gesagt, ich möchte nils sicher handhaben, aber in der nächsten Zeile haben Sie gesagt, nein, ich weiß nicht !!!

Der Compiler würde eine Warnung geben:

Wird ein erzwungener Downcast auf 'T' als optional behandelt, wird er niemals 'Null' erzeugen.

Eine Alternative besteht darin, den ? zu entfernen.

func returnSomething<T>() -> T{ // I can't handle nils

    return UIViewController() as! T // I will crash on nils
}

Nachdem dies gesagt wurde, ist es wahrscheinlich der beste Weg, keinen Gewaltwurf anzuwenden und einfach Folgendes zu tun:

func returnSomething<T>() -> T?{ // I can handle nils

    return UIViewController() as? T // I won't crash on nils
}
3
Honey

Es sieht so aus, als wäre über diese Warnung in einigen Jahren ein Fehler aufgetreten ... https://bugs.Swift.org/browse/SR-4209 Es zeigt sich also auch in Situationen, in denen offensichtlich ist, was du machst und recht. 

0
Renetik