Frage Downcasting-Optionen in Swift: as? Typ oder als! Art?


Angesichts der folgenden in Swift:

var optionalString: String?
let dict = NSDictionary()

Was ist der praktische Unterschied zwischen den folgenden zwei Aussagen:

optionalString = dict.objectForKey("SomeKey") as? String

vs

optionalString = dict.objectForKey("SomeKey") as! String?

76
2017-09-07 09:07


Ursprung


Antworten:


Der praktische Unterschied ist dies:

var optionalString = dict["SomeKey"] as? String

optionalString wird eine Variable vom Typ sein String?. Wenn der zugrunde liegende Typ etwas anderes als a ist String Dies wird harmlos nur zuweisen nil zum optionalen.

var optionalString = dict["SomeKey"] as! String?

Das sagt, ich kennt dieses Ding ist ein String?. Dies wird auch führen optionalString Typ sein String?, aber Es wird abstürzen, wenn der zugrunde liegende Typ etwas anderes ist.

Der erste Stil wird dann mit verwendet if let das optionale entfernen:

if let string = dict["SomeKey"] as? String {
    // If I get here, I know that "SomeKey" is a valid key in the dictionary, I correctly
    // identified the type as String, and the value is now unwrapped and ready to use.  In
    // this case "string" has the type "String".
    print(string)
}

121
2017-09-07 12:28



Um zu verdeutlichen, was Vacawama gesagt hat, hier ist ein Beispiel ...

Swift 3.0:

import UIKit

let str_value:    Any   = String("abc")!
let strOpt_value: Any?  = String("abc")!
let strOpt_nil:   Any?  = (nil as String?)
let int_value:    Any   = Int(1)
let intOpt_value: Any?  = Int(1)
let intOpt_nil:   Any?  = (nil as Int?)

// as String
//str_value     as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//strOpt_value  as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//strOpt_nil    as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//int_value     as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//intOpt_value  as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//intOpt_nil    as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?

// as? String
  str_value     as? String // == "abc"
  strOpt_value  as? String // == "abc"
  strOpt_nil    as? String // == nil
  int_value     as? String // == nil
  intOpt_value  as? String // == nil
  intOpt_nil    as? String // == nil

// as! String
  str_value     as! String // == "abc"
  strOpt_value  as! String // == "abc"
//strOpt_nil    as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value.
//int_value     as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
//intOpt_value  as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
//intOpt_nil    as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value.

// as String?
//str_value     as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//strOpt_value  as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//strOpt_nil    as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//int_value     as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//intOpt_value  as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//intOpt_nil    as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?

// as? String?
//str_value     as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  strOpt_value  as? String? // == "abc"
  strOpt_nil    as? String? // == nil
//int_value     as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  intOpt_value  as? String? // == nil
  intOpt_nil    as? String? // == nil

// as! String?
//str_value     as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  strOpt_value  as! String? // == "abc"
  strOpt_nil    as! String? // == nil
//int_value     as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
//intOpt_value  as! String? // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
  intOpt_nil    as! String? // == nil

// let _ = ... as String
//if let _ = str_value    as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_nil   as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = int_value    as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_nil   as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?

// let _ = ... as? String
if let _ = str_value    as? String { true } // true
if let _ = strOpt_value as? String { true } // true
if let _ = strOpt_nil   as? String { true } // false
if let _ = int_value    as? String { true } // false
if let _ = intOpt_value as? String { true } // false
if let _ = intOpt_nil   as? String { true } // false

// let _ = ... as! String
//if let _ = str_value    as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = strOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = strOpt_nil   as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = int_value    as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = intOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = intOpt_nil   as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'

// let _ = ... as String?
//if let _ = str_value    as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//if let _ = strOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_nil   as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = int_value    as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//if let _ = intOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_nil   as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?

// let _ = ... as? String?
//if let _ = str_value    as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = strOpt_value as? String? { true } // true
  if let _ = strOpt_nil   as? String? { true } // true
//if let _ = int_value    as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = intOpt_value as? String? { true } // false
  if let _ = intOpt_nil   as? String? { true } // true

// let _ = ... as! String?
//if let _ = str_value    as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = strOpt_value as! String? { true } // true
  if let _ = strOpt_nil   as! String? { true } // false
//if let _ = int_value    as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
//if let _ = intOpt_value as! String? { true } // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
  if let _ = intOpt_nil   as! String? { true } // false

Swift 2.0:

import UIKit

let str:    AnyObject   = String("abc")
let strOpt: AnyObject?  = String("abc")
let strNil: AnyObject?  = (nil as String?)
let int:    AnyObject   = Int(1)
let intOpt: AnyObject?  = Int(1)
let intNil: AnyObject?  = (nil as Int?)

str    as? String // == "abc"
strOpt as? String // == "abc"
strNil as? String // == nil
int    as? String // == nil
intOpt as? String // == nil
intNil as? String // == nil

str    as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?'
strOpt as! String? // == "abc"
strNil as! String? // == nil
int    as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?'
intOpt as! String? // Run-Time Error: Could not cast value of type '__NSCFNumber' to 'NSString'
intNil as! String? // == nil

10
2018-03-27 18:10



as? Types - bedeutet, dass der Down-Casting-Prozess optional ist. Der Prozess kann erfolgreich sein oder nicht (das System gibt Nil zurück, wenn das Down-Casting fehlschlägt). Jeder Fall wird nicht abstürzen, wenn das Down-Casting fehlschlägt.

as! Type? - Hier sollte der Prozess des Down Castings erfolgreich sein (! weist darauf hin, dass) . Das abschließende Fragezeichen zeigt an, ob das Endergebnis gleich Null sein kann oder nicht.

Weitere Informationen zu "!" und "?"

Nehmen wir 2 Fälle

  1. Erwägen:

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
    

    Hier wissen wir nicht, ob das Ergebnis der Abwärtswandlung der Zelle mit dem Identifikator "Cell" zu UITableViewCell erfolgreich ist oder nicht. Wenn es nicht erfolgreich ist, gibt es null zurück (so vermeiden wir hier einen Absturz). Hier können wir tun, wie unten angegeben.

    if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell {
        // If we reached here it means the down casting was successful
    }
    else {
        // unsuccessful down casting
    }
    

    Also lassen Sie uns daran denken - If ? Es bedeutet, dass wir nicht sicher sind, ob der Wert null ist oder nicht (das Fragezeichen kommt, wenn wir die Dinge nicht wissen).

  2. Vergleichen Sie das mit:

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell. 
    

    Hier sagen wir dem Compiler, dass Down Casting erfolgreich sein sollte. Wenn es fehlschlägt, stürzt das System ab. Also geben wir ! wenn wir sicher sind, dass der Wert nicht null ist.


10
2018-06-04 17:21



Sie sind zwei verschiedene Formen von Downcasting in Swift.

(as?), die bekannt ist, die sein Bedingte Form, gibt einen optionalen Wert des Typs zurück, zu dem Sie einen Downcast durchführen möchten.

Sie können es verwenden, wenn Sie nicht sicher sind, ob der Downcast erfolgreich ist.   Diese Form des Operators liefert immer einen optionalen Wert, und   Der Wert ist Null, wenn der Downcast nicht möglich war. Das ermöglicht   Sie müssen nach einem erfolgreichen Downcast suchen.


(as!), die bekannt ist, die sein Erzwungene Form, versucht den Niedergeschlagenen und erzwingt das Ergebnis als eine einzige zusammengesetzte Aktion.

Du solltest es benutzen NUR wenn du sicher bist, dass der Niedergeschlagene es tun wird   immer gelingen. Diese Form des Bedieners löst einen aus Laufzeit   Error wenn Sie versuchen, auf einen falschen Klassentyp herabzustufen.

Für weitere Details, überprüfen Sie bitte Geben Sie Casting ein Abschnitt der Apple-Dokumentation.


7
2017-07-08 10:20



  • as verwendet für Upcasting und Typ Gießen zu überbrückten Typ
  • as? Wird für sicheres Casting verwendet, Rückgabe NULL, falls fehlgeschlagen
  • as! verwendet, um Casting zu erzwingen, Absturz wenn fehlgeschlagen

Hinweis:

  • as! kann den Rohtyp nicht optional darstellen

Beispiele:

let rawString: AnyObject = "I love swift"
let optionalString: AnyObject? = "we love swift"
let nilString: AnyObject? = (nil as String?)

let rawInt: AnyObject = Int(3)
let optionalInt: AnyObject? = Int(3)
let nilInt: AnyObject? = (nil as Int?)

Beispiel

var age: Int? = nil
var height: Int? = 180

Durch Hinzufügen von a ? unmittelbar nach dem Datentyp teilt man dem Compiler mit, dass die Variable eine Zahl enthalten kann oder nicht. Ordentlich! Beachten Sie, dass es nicht wirklich sinnvoll ist, optionale Konstanten zu definieren - Sie können ihren Wert nur einmal festlegen und daher können Sie sagen, ob ihr Wert gleich Null ist oder nicht.

Wann sollten wir "?" und wann "!"

Nehmen wir an, wir haben eine einfache UIKit-App. Wir haben einen Code in unserem View-Controller und möchten darüber einen neuen View-Controller präsentieren. und wir müssen entscheiden, die neue Ansicht auf dem Bildschirm mit Navigation Controller zu schieben.

Wie wir wissen, verfügt jede ViewController-Instanz über einen Eigenschaftsnavigationscontroller. Wenn Sie eine navigationscontrollerbasierte App erstellen, wird diese Eigenschaft des Master-View-Controllers Ihrer App automatisch festgelegt, und Sie können sie verwenden, um Controller zu pushen oder anzuzeigen. Wenn Sie eine einzelne App-Projektvorlage verwenden, wird kein Navigations-Controller automatisch für Sie erstellt, sodass der Standard-View-Controller Ihrer App nichts in der Eigenschaft navigationController gespeichert hat.

Ich bin sicher, Sie haben bereits vermutet, dass dies genau der Fall für einen optionalen Datentyp ist. Wenn Sie UIViewController überprüfen, sehen Sie, dass die Eigenschaft wie folgt definiert ist:

var navigationController: UINavigationController? { get }

Lass uns zu unserem Anwendungsfall zurückkehren. Wenn Sie sicher sind, dass Ihr View-Controller immer über einen Navigations-Controller verfügt, können Sie die Unwrap-Funktion aktivieren:

controller.navigationController!.pushViewController(myViewController, animated: true)

Wenn du ein! Hinter dem Namen der Eigenschaft sagt man dem Compiler Es ist mir egal, dass diese Eigenschaft optional ist. Ich weiß, dass, wenn dieser Code ausgeführt wird, immer ein Wertspeicher vorhanden ist. Behandeln Sie diesen Optional wie einen normalen Datentyp. Ist das nicht schön? Was würde passieren, wenn es keinen Navigationscontroller für Ihren View Controller gibt? Wenn Sie vorschlagen, dass immer ein Wert in NavigationController gespeichert wird, war falsch? Ihre App wird abstürzen. Einfach und hässlich.

Also, benutze! Nur wenn Sie sich zu 101% sicher sind, dass dies sicher ist.

Wie wäre es, wenn Sie nicht sicher sind, dass es immer einen Navigationscontroller geben wird? Dann kannst du verwenden? anstelle einer !:

controller.navigationController?.pushViewController(myViewController, animated: true)

Was zum ? hinter dem Namen der Eigenschaft sagt der Compiler ist Ich weiß nicht, ob diese Eigenschaft nil oder einen Wert enthält, also: Wenn es Wert hat, benutze es, und betrachte den ganzen Ausdruck nichtig. Wirklich das? ermöglicht Ihnen, diese Eigenschaft nur für den Fall zu verwenden, dass ein Navigationscontroller vorhanden ist. Nein, wenn irgendwelche Prüfungen oder Castings jeglicher Art stattfinden. Diese Syntax ist perfekt, wenn es Ihnen egal ist, ob Sie einen Navigationscontroller haben oder nicht, und nur dann etwas tun möchten, wenn dies der Fall ist.

Vielen Dank an Fantageek


4
2018-03-31 20:20



Vielleicht hilft dieses Codebeispiel jemandem, das Prinzip zu verstehen:

var dict = [Int:Any]()
dict[1] = 15

let x = dict[1] as? String
print(x) // nil because dict[1] is an Int

dict[2] = "Yo"

let z = dict[2] as! String?
print(z) // optional("Yo")
let zz = dict[1] as! String // crashes because a forced downcast fails


let m = dict[3] as! String?
print(m) // nil. the forced downcast succeeds, but dict[3] has no value

2
2017-07-03 18:54



Die erste ist eine "conditional cast" (schauen Sie unter "type-casting operators" in der Dokumentation, die ich verlinkt habe). Wenn die Umwandlung erfolgreich ist, wird der Wert des Ausdrucks in ein optionales Feld eingeschlossen und zurückgegeben, andernfalls wird der Wert nil zurückgegeben.

Die zweite bedeutet, dass optionalString ein String-Objekt sein könnte oder es könnte nil sein.

Weitere Informationen finden Sie in dieser verwandten Frage.


0
2017-09-07 09:14



Es ist vielleicht am einfachsten, sich das Muster für diese Operatoren in Swift wie folgt zu merken: ! impliziert "dies könnte Falle", während ? zeigt an, dass dies "null" sein könnte.

beziehen auf: https://developer.apple.com/swift/blog/?id=23


0
2018-03-04 14:06