it-swarm.com.de

Warum kann kein beliebiger beliebig sein?

In Swift kann ich eine Konstante vom Typ Any deklarieren und eine String einfügen.

let any: Any = "hello world"

Gut. Auf der anderen Seite kann ich kann keinen nil-Wert in any setzen, da er nicht optional ist.

let any: Any = nil

error: nil cannot initialize specified type 'Any' (aka 'protocol<>')
let any: Any = nil
              ^

Perfekt. Aber warum erlaubt der Compiler mir, den folgenden Code zu schreiben?

let couldBeNil: String? = nil
let any: Any = couldBeNil
print(any) // nil

Folgt Any nicht der Swift-Regel, dass nur ein optionales var/let mit nil gefüllt werden kann?

Getestet mit Xcode Playground 7.2 + Swift 2.1.1

31
Luca Angeletti

TL; DR; Optionals in Swift werden vom Compiler in Optional enum-Instanzen übersetzt, und da Any auf einen beliebigen Wert abgebildet werden kann, können Optionsals gespeichert werden.


Wie stellt Swift Optionals dar? Dies geschieht durch die Zuordnung von SomeType? zu einer konkreten Implementierung der Optional-Enumeration:

Int? => Optional<Int>
String? => Optional<String>

Eine vereinfachte Deklaration von Optional sieht folgendermaßen aus:

enum Optional<T> {
    case none    // nil
    case some(T) // non-nil
}

Eine Variable des Typs Any kann nun einen Aufzählungswert (oder einen anderen Wert oder sogar Metatyp-Informationen) enthalten. Daher sollte sie beispielsweise einen nil-String (aka String?.none, aka Optional<String>.none) enthalten können.

Mal sehen, was passiert. Wie aus der Optional-Deklaration hervorgeht, entspricht nil für alle Typen dem .none-Enumerationsfall:

nil == Optional<String>.none // true
nil == Optional<Int>.none    // true
[Double]?.none == nil        // also true

Theoretisch sollten Sie daher nil einer als Any deklarierten Variablen zuweisen können. Der Compiler erlaubt dies jedoch nicht.

Aber warum lässt der Compiler nicht nil einer Any-Variablen zu? Dies liegt daran, dass es nicht möglich ist, auf welchen Typ der Fall .none enum zugeordnet werden kann. Optional ist ein generisches Enum, daher muss der generische Parameter T gefüllt werden, und die nil-Ebene ist zu breit. Welchen .none-Wert sollte es verwenden? Die von Int, die von String, eine andere?

Dies gibt eine Fehlermeldung aus, die den obigen Absatz unterstützt:

let nilAny: Any = nil // error: nil cannot initialize specified type 'Any' (aka 'protocol<>')

Der folgende Code funktioniert und entspricht der Zuweisung einer nil:

let nilAny: Any = Optional<Int>.none

, da die Variable Any tatsächlich einen gültigen Wert der Optional-Enumeration enthält.

Indirekte Zuweisungen funktionieren ebenfalls, da hinter den Kulissen nil in Optional<Type>.none konvertiert wird.

var nilableBool: Bool? // nilableBool has the Optional<Bool>.none value
var nilBoolAsAny: Any = nilableBool // the compiler has all the needed type information from nilableBool

Im Gegensatz zu anderen Sprachen entspricht nil in Swift einem konkreten Wert. Es benötigt jedoch einen Typ, mit dem der Compiler wissen kann, welchen Optional<T>.none er zuordnen soll. Wir können uns das Keyword als Zuckersyntax vorstellen.

57
Cristik

Aus den Swift-Dokumenten: "Any: Das Protokoll, dem alle Typen implizit entsprechen."

d.h. typealias Any = protocol<>

Wenn Sie also String? deklarieren, könnten Sie sich Optional<String> vorstellen, wobei Optional<T> folgendermaßen implementiert werden kann:

enum Optional<T> {
  case Some(T), None
}

Und Enumerationen sind Typen, daher entsprechen sie dem Any-Protokoll. Wie in den Kommentaren angegeben, ist nil kein Typ (und entspricht daher nicht Any, es ist das Fehlen eines Werts (und in diesem Fall hat kein Wert keinen Typ).

Optionals werden in die Sprache eingebettet und sind keine regulären Enumerationen, es steht jedoch fest, dass sie mit Any übereinstimmen, während eine typlose nil dies nicht tut. 

8
Alex Popov

Für jeden, der nach einem Weg sucht, zu überprüfen, ob Anynil enthält:

Wenn Sie po value aufrufen, wird Ihnen mitgeteilt, dass eine Any-Variable den Wert nil enthält, die folgende Zeile funktioniert jedoch nicht:

if value == nil { ... } // Returns false and generates a warning

Sie müssen ihn vor der Überprüfung zuerst in einen unbestellbaren Wert umwandeln, z. B. wenn Any wahrscheinlich eine Date enthält:

if Optional<Date>.none == value as? Date { ... } // Returns true and no warning

Hinweis: Ich habe es nicht getestet, daher keine Garantie, aber die Zeile oben könnte auch passen, wenn geschrieben wird als:

if Optional<Any>.none == value as? Any { ... }
1
Departamento B