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 Formularas
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?
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 temporaryString
nil
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.
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 nil
s 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
}
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.