web-dev-qa-db-ja.com

Swiftのダウンキャスティングオプション:as?タイプ、またはas!タイプ?

Swiftで次の場合:

var optionalString: String?
let dict = NSDictionary()

次の2つのステートメントの実際の違いは何ですか。

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

vs

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

実際の違いは次のとおりです。

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

optionalStringString?型の変数になります。基になる型がString以外の場合、これは無害にnilをオプションに割り当てます。

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

これは、IknowこのことはString?であると言います。これもoptionalStringString?型になります。but基になる型が他の型である場合はクラッシュします。

次に、最初のスタイルをif letとともに使用して、オプションを安全にアンラップします。

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)
}
132
vacawama

バカワマが言ったことを明確にするために、ここに例があります...

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
11
Senseful

as? Types-ダウンキャストプロセスがオプションであることを意味します。プロセスは成功する場合も失敗する場合もあります(ダウンキャストが失敗した場合、システムはnilを返します)。ダウンキャストが失敗した場合、どの方法でもクラッシュしません。

as! Type?-ここで、ダウンキャストのプロセスは成功するはずです(!はそれを示します)。最後の疑問符は、最終結果がゼロになるかどうかを示します。

「!」および「?」に関する詳細情報

2つのケースを取り上げましょう

  1. 考慮してください:

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

    ここでは、識別子「Cell」のセルをUITableViewCellにダウンキャストした結果が成功したかどうかはわかりません。失敗した場合は、nil(を返すため、ここでクラッシュを回避します)。ここでは、次のように実行できます。

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

    このように覚えておきましょう-?の場合、値がnilかどうかわからないことを意味します(疑問符は、物事がわからないときに表示されます)。

  2. それとは対照的に:

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

    ここで、ダウンキャストが成功するはずであることをコンパイラに伝えます。失敗すると、システムがクラッシュします。そのため、値が非nilであることが確実なときに!を与えます。

11
jishnu bala

これらは、SwiftのDowncastingの2つの異なる形式です。

as?、これはConditional Form、ダウンキャストしようとしているタイプのオプションの値を返します。

ダウンキャストが成功するかどうかわからない場合に使用できます。この形式の演算子は常にオプションの値を返し、ダウンキャストが不可能な場合、値はnilになります。これにより、ダウンキャストの成功を確認できます。


as!、これはForced Form、ダウンキャストを試み、結果を単一の複合アクションとして強制的にアンラップします。

ダウンキャストが常に成功することが確実な場合は、のみを使用する必要があります。この形式の演算子は、誤ったクラスタイプにダウンキャストしようとすると、ランタイムエラーをトリガーします。

詳細については、AppleのドキュメントのType Castingセクションを確認してください。

8
Scott Zhu
  • asは、アップキャストおよびブリッジ型への型キャストに使用されます
  • as?は安全なキャストに使用され、失敗した場合はnilを返します
  • as!はキャストの強制に使用され、失敗するとクラッシュします

注意:

  • as!は生のタイプをオプションにキャストできません

例:

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

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

データ型の直後にを追加することにより、変数に数値が含まれるかどうかがコンパイラーに通知されます。きちんとした!オプションの定数を定義することは実際には意味をなさないことに注意してください。値を設定できるのは1回だけなので、値がゼロかどうかを判断できます。

「?」を使用する必要がある場合そして「!」

uIKitベースのシンプルなアプリがあるとします。 View Controllerにいくつかのコードがあり、その上に新しいView Controllerを表示したいと考えています。そして、Navigation Controllerを使用して画面に新しいビューをプッシュすることを決定する必要があります。

知っているように、すべてのViewControllerインスタンスにはプロパティナビゲーションコントローラーがあります。 Navigation Controllerベースのアプリを構築している場合、アプリのマスターView Controllerのこのプロパティは自動的に設定され、View Controllerのプッシュまたはポップに使用できます。単一のアプリプロジェクトテンプレートを使用する場合、Navigation Controllerは自動的に作成されないため、アプリのデフォルトのView ControllerにはnavigationControllerプロパティに何も保存されません。

これは、オプションのデータ型の場合とまったく同じであると既に推測していると思います。 UIViewControllerをチェックすると、プロパティが次のように定義されていることがわかります。

var navigationController: UINavigationController? { get }

それでは、ユースケースに戻りましょう。 View Controllerに常にNavigation Controllerがあることを知っている場合は、先に進み、強制的にアンラップできます:

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

あなたが置くとき!コンパイラに伝えるプロパティ名の後ろにこのプロパティがオプションであることは気にしませんこのオプションを通常のデータ型と同様に扱います。 View ControllerにNavigation Controllerがない場合はどうなりますか? navigationControllerに常に値が保存されるという提案が間違っていましたか?アプリがクラッシュします。そのようにシンプルでい。

だから、使用してください!これが安全であることを101%確信している場合のみ。

ナビゲーションコントローラーが常に存在するかどうかわからない場合はどうでしょうか。次に使用できますか? !:の代わりに

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

なに?プロパティ名の後ろには、コンパイラがであることが示されています式全体を考慮nil。事実上? Navigation Controllerがある場合にのみ、そのプロパティを使用できます。あらゆる種類のチェックまたはあらゆる種類の鋳物の場合、いいえ。この構文は、Navigation Controllerを使用しているかどうかを気にせず、存在する場合にのみ何かをしたいときに最適です。

Fantageek に多大な感謝

7
swiftBoy

たぶん、このコード例は誰かが原理を理解するのに役立つでしょう:

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
3
smileBot

最初は「条件付きキャスト」です(リンクしたドキュメントの「型キャスト演算子」の下を見てください) 。キャストが成功した場合、式の値はオプションでラップされて返されます。それ以外の場合、返される値はnilです。

2番目は、optionalStringが文字列オブジェクトであったり、nilであったりすることを意味します。

この関連する質問に詳細があります

0