Pytanie Downcasting optionsals w Swift: as? Wpisz lub jako! Rodzaj?


Biorąc pod uwagę następujące informacje w Swift:

var optionalString: String?
let dict = NSDictionary()

Jaka jest praktyczna różnica między następującymi dwoma stwierdzeniami:

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

vs

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

76
2017-09-07 09:07


pochodzenie


Zobacz rzutowanie typu w Swift 2 fantageek.com/blog/2015/09/23/type-casting-in-swift - onmyway133
Aslo Zobacz The as! Operator firmy Apple - Honey


Odpowiedzi:


Różnica praktyczna jest następująca:

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

optionalString będzie zmienną typu String?. Jeśli podstawowym typem jest coś innego niż String to nieszkodliwe po prostu przypisać nil do opcjonalnego.

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

To mówi, ja wiedzieć to jest String?. To również spowoduje optionalString bycie typem String?, ale ulegnie awarii, jeśli podstawowym typem jest coś innego.

Pierwszy styl jest następnie używany z if let aby bezpiecznie rozwinąć opcjonalne:

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



Czy pierwsza metoda nie jest zawsze lepsza? Oba zwracają opcjonalny typ String? Wydaje się, że druga metoda robi to samo co pierwsza, ale może się zawieść, jeśli odrzucenie nie powiedzie się. Dlaczego więc w ogóle go używać? - Sikander
Tak, @Sikander, pierwszy jest zawsze lepszy. Nigdy nie skorzystałbym z drugiego. - vacawama


Aby wyjaśnić, co powiedział vacawama, oto przykład ...

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



+1 dla Twojego przykładu, ale możesz mi wyjaśnić ten sam przykład, którego użyjesz! w miejsce jak? podczas downcastingu niech komórka = tableView.dequeueReusableCellWithIdentifier ("Cell") as! UITableViewCell..i zgadnij jako? wystarczało, dlaczego potrzebna była jakaś! - Anish 웃
niech cell = tableView.dequeueReusableCellWithIdentifier ("Cell") jako? UITableViewCell. - tutaj nie wiemy, czy wynik rzucania komórki z identyfikatorem "Komórka" do UITableViewCell jest zerowy czy nie. Jeśli nill to zwróci wartość Nill (więc unikamy tu awarii). - jishnu bala
ciekawy, intNil as! String? // ==nil nie powoduje awarii !!! ???, jako Opcjonalne <Int> .None różni się od Opcjonalnego <String> .None - onmyway133
dlaczego jesteś downcast as? do String? Dlaczego go nie spuszczasz String? ? Dlaczego nie spuszczasz się as! do String? - Honey
Próba zrobienia tego placu zabaw w Swift 3, ale musisz go użyć Any zamiast AnyObject - Honey


as? Types - oznacza, że ​​proces odlewania w dół jest opcjonalny. Proces może zakończyć się powodzeniem lub nie (system wróci do zera, jeśli rzutowanie zakończy się niepowodzeniem). W żaden sposób nie nastąpi awaria w przypadku niepowodzenia rzutowania.

as! Type? - Tutaj proces powalania powinien zakończyć się sukcesem (! wskazuje, że). Kończący się znak zapytania wskazuje, czy wynik końcowy może być zerowy, czy też nie.

Więcej informacji na temat "!" i "?"

Weźmy 2 przypadki

  1. Rozważać:

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

    Tutaj nie wiemy, czy wynik rzucania komórki z identyfikatorem "Komórka" na UITableViewCell jest sukcesem czy nie. Jeśli się nie powiedzie, zwróci zero (a więc unikniemy awarii tutaj). Tutaj możemy zrobić, jak podano poniżej.

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

    Więc pamiętajmy o tym tak - Jeśli ? oznacza to, że nie jesteśmy pewni, czy wartość jest zerowa, czy nie (znak zapytania pojawia się, gdy czegoś nie wiemy).

  2. Porównaj to z:

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

    Tutaj mówimy kompilatorowi, że odlewanie w dół powinno zakończyć się powodzeniem. Jeśli się nie uda, system ulegnie awarii. Więc dajemy ! kiedy jesteśmy pewni, że wartość nie jest zerowa.


10
2018-06-04 17:21





Są to dwie różne formy Downcasting w Swift.

(as?), który jest znany jako Forma warunkowa, zwraca opcjonalną wartość typu, który próbujesz spuścić na dół.

Możesz go użyć, gdy nie masz pewności, czy downcast się powiedzie.   Ta forma operatora zawsze zwraca opcjonalną wartość, oraz   wartość będzie zerowa, jeśli odrzucenie nie było możliwe. To umożliwia   aby sprawdzić udany downcast.


(as!), który jest znany jako Przymusowa forma, podejmuje próbę odrzucenia i wymuszenia - rozpakowuje wynik jako pojedynczą akcję złożoną.

Powinieneś go użyć TYLKO kiedy jesteś pewny, że downcast będzie   zawsze się udaje. Ta forma operatora wyzwoli środowisko wykonawcze   błąd jeśli próbujesz obniżyć do niewłaściwego typu klasy.

Aby uzyskać więcej informacji, sprawdź Wpisz Casting sekcja dokumentacji firmy Apple.


7
2017-07-08 10:20





  • as używane do upcastingu i odlewania typu do typu mostowego
  • as? służy do bezpiecznego rzucania, w razie niepowodzenia zwraca zero
  • as! używany do wymuszania rzucania, awaria, jeśli się nie powiodła

Uwaga:

  • as! nie można rzutować typu surowego na opcjonalny

Przykłady:

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

Przykład

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

Dodając ? bezpośrednio po typie danych informujesz kompilator, że zmienna może zawierać liczbę lub nie. Schludny! Zauważ, że naprawdę nie ma sensu definiowanie stałych opcjonalnych - możesz ustawić ich wartość tylko raz, a zatem będziesz w stanie określić, czy ich wartość będzie zerowa czy nie.

Kiedy powinniśmy użyć "?" i kiedy "!"

załóżmy, że mamy prostą aplikację opartą na UIKit. mamy jakiś kod w naszym kontrolerze widoku i chcemy przedstawić na nim nowy kontroler widoku. i musimy zdecydować, aby przesunąć nowy widok na ekranie za pomocą kontrolera nawigacyjnego.

Jak wiemy, każda instancja ViewController ma kontroler nawigacji właściwości. Jeśli tworzysz aplikację opartą na kontrolerze nawigacyjnym, ta właściwość kontrolera widoku głównego aplikacji jest ustawiana automatycznie i możesz jej użyć do sterowania kontrolkami push lub pop view. Jeśli korzystasz z szablonu pojedynczej aplikacji - nie będzie automatycznie utworzony kontroler nawigacyjny, więc domyślny kontroler widoku aplikacji nie będzie zawierał niczego zapisanego we właściwości navigationController.

Jestem pewien, że już zgadłeś, że tak jest w przypadku opcjonalnego typu danych. Jeśli zaznaczysz UIViewController, zobaczysz, że właściwość jest zdefiniowana jako:

var navigationController: UINavigationController? { get }

Wróćmy więc do naszego przypadku użycia. Jeśli wiesz, że kontroler podglądu zawsze będzie miał kontroler nawigacyjny, możesz go zmusić do rozwinięcia:

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

Kiedy umieścisz! za nazwą właściwości mówisz kompilatorowi Nie obchodzi mnie, że ta właściwość jest opcjonalna, wiem, że kiedy ten kod będzie wykonywany, zawsze będzie przechowywany zapis wartości, więc traktuj to Opcjonalnie jak normalny typ danych. Czy to nie jest miłe? Co by się stało, gdyby nie kontroler nawigacyjny do kontrolera widoku? Jeśli sugerujesz, że zawsze będzie zapisana wartość w navigationController, było źle? Twoja aplikacja zawiesi się. Proste i brzydkie.

Więc użyj! tylko jeśli masz 101% pewności, że jest to bezpieczne.

A jeśli nie masz pewności, że zawsze będzie kontroler nawigacyjny? Wtedy możesz użyć? zamiast !:

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

Co ? za nazwą właściwości mówi kompilator Nie wiem, czy ta właściwość zawiera zero, czy wartość, więc: jeśli ma wartość, użyj go, a odwrotnie po prostu rozważ całe wyrażenie zerowe. Skutecznie? umożliwia korzystanie z tej właściwości w przypadku, gdy istnieje kontroler nawigacyjny. Nie, jeśli jakiekolwiek kontrole lub castingi jakiegokolwiek rodzaju. Ta składnia jest idealna, gdy nie obchodzi cię, czy masz kontroler nawigacyjny, czy nie, i chcesz zrobić coś tylko wtedy, gdy jest.

Ogromne dzięki Fantageek


4
2018-03-31 20:20





Może ten przykład kodu pomoże komuś pogadać z zasadą:

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



Niech z2 = dict [2] as! String // "Yo" (nie opcjonalne) - Jay


Pierwszym z nich jest "rzutowanie warunkowe" (spójrz pod "operatorami rzutowania typu" w dokumentacji, którą połączyłem). Jeśli rzutowanie powiedzie się, wartość wyrażenia jest zawijana w opcjonalny i zwracany, w przeciwnym razie zwrócona wartość będzie zerowa.

Drugi oznacza, że ​​opcjaString może być obiektem typu string lub może być zerowa.

Więcej informacji można znaleźć w tym pokrewnym pytaniu.


0
2017-09-07 09:14





Najłatwiej jest zapamiętać wzór dla tych operatorów w Swift: ! oznacza, że ​​"to może być pułapką", podczas gdy ? wskazuje "to może nie być".

odnosić się do: https://developer.apple.com/swift/blog/?id=23


0
2018-03-04 14:06





Jestem początkującym w Swift i piszę ten przykład, próbując wyjaśnić, gdy rozumiem "opcje". Jeśli się mylę, popraw mnie.

Dzięki.


class Optional {

    var lName:AnyObject! = "1"

    var lastName:String!
}

let obj = Optional()

print(obj.lName)

print(obj.lName!)

obj.lastName = obj.lName as? String

print(obj.lastName)

(1): obj.lastName = obj.lName as! String

vs

(2): obj.lastName = obj.lName as? String

Odp: (1) Tutaj programista jest pewny tego “obj.lName” zawiera obiekt typu string. Po prostu daj tę wartość “obj.lastName”.

Teraz, jeśli programista jest poprawny oznacza "obj.lName" jest obiektem typu string, wtedy nie ma problemu. "obj.lastName" zostanie ustawione na tę samą wartość.

Ale jeśli programista jest niewłaściwy, oznacza to "obj.lName" nie jest obiektem typu string, tzn. zawiera jakiś inny obiekt typu, taki jak "NSNumber" itd. Następnie CRASH (błąd czasu pracy).

(2) Programista nie jest tego pewien “obj.lName” zawiera obiekt typu string lub dowolny inny obiekt typu. Ustaw tę wartość na “obj.lastName” jeśli jest to typ łańcucha.

Teraz, jeśli programista jest poprawny oznacza “obj.lName” jest obiektem typu string, wtedy nie ma problemu. “obj.lastName” ustawi tę samą wartość.

Ale jeśli programista jest nieprawidłowy oznacza to, że obiekt obj.lName nie jest obiektem typu string, tzn. Zawiera jakiś inny obiekt typu, taki jak "NSNumber" itd. Następnie “obj.lastName” ustawi wartość zerową. No No Crash (Happy :)


-1
2017-09-02 10:57