Swiftでのオプションのダウンキャスト:as?タイプ、またはとして!タイプ?


回答:


142

実際的な違いはこれです:

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

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

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

これは言う、私これがであることを知っていString?ます。これもoptionalStringの型String?になります、基になる型が他のものである場合はクラッシュします。

次に、最初のスタイルを使用し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)
}

最初の方法は常により良いのではないですか?どちらもString型のオプションを返しますか?2番目の方法は最初の方法と同じように見えますが、ダウンキャストが失敗するとクラッシュする可能性があります。では、なぜそれを使用するのでしょうか。
シカンデル

6
はい、@ Sikander、最初のものは常に優れています。私は二番目を使うことはありません。
vacawama 2016年

14

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

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

「!」に関する詳細情報 そして「?」

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

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

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

あなたの例の+1ですが、同じ例を使って説明してもらえますか?の代わりに?let cell = tableView.dequeueReusableCellWithIdentifier( "Cell")としてダウンキャストしながら!UITableViewCell..iと思いますか?asの必要性があった理由は十分でした!
Anish Parajuli

cell = tableView.dequeueReusableCellWithIdentifier( "Cell")として UITableViewCell。-ここでは、識別子「Cell」を持つセルをUITableViewCellにダウンキャストした結果がnillかどうかはわかりません。nillの場合、nillを返します(ここではクラッシュを回避します)。
jishnu bala

興味深い、intNil as! String? // ==nilクラッシュを引き起こさない!!! ???、Optional <Int> .NoneはOptional <String>
.None

なぜダウンキャストas?するのStringですか?にダウンキャストしてみませんString?か?にダウンキャストas!してみませんStringか?
ハニー、

Swift 3でこの遊び場を試してみますが、Any代わりに使用する必要がありますAnyObject
Honey

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

注意:

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

例:

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回だけなので、値をnilにするかどうかを指定できます。

「?」をいつ使うべきか そして「!」

UIKitベースのシンプルなアプリがあるとします。ビューコントローラーにコードがあり、その上に新しいビューコントローラーを表示したいと考えています。ナビゲーションコントローラを使用して、画面に新しいビューをプッシュすることを決定する必要があります。

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

これがOptionalデータ型の場合とまったく同じであることは、すでにご想像のとおりと思います。UIViewControllerをチェックすると、プロパティが次のように定義されていることがわかります。

var navigationController: UINavigationController? { get }

それでは、ユースケースに戻りましょう。ビューコントローラーに常にナビゲーションコントローラーがあることがわかっている場合は、先に進んで強制的にアンラップできます。

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

あなたが置くとき!プロパティ名の後ろでコンパイラーにこのプロパティがオプションであることは気にしません、私はこのコードが実行されるとき常に値ストアがあるので、このOptionalを通常のデータ型のように扱います。まあそれはいいことではないですか?ビューコントローラーへのナビゲーションコントローラーがない場合はどうなりますか?あなたがnavigationControllerに保存されている値が常に存在することを示唆している場合は間違っていましたか?アプリがクラッシュします。そのようにシンプルで醜い。

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

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

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

なに?プロパティ名の後ろは、このプロパティにnilと値のどちらが含まれているかがわからないことをコンパイラに通知するため、値がある場合はそれを使用し、そうでなければ式全体をnilと見なします。効果的に?ナビゲーションコントローラがある場合にのみ、そのプロパティを使用できます。あらゆる種類のチェックや、あらゆる種類のキャスティングはありません。この構文は、ナビゲーションコントローラーの有無に関係なく、存在する場合にのみ何かを実行する場合に最適です。

Fantageekに感謝


8

これらはSwiftの2つの異なる形式のダウンキャスティングです。

as?条件付きフォームであることがわかっており、ダウンキャストしようとしている型のオプションの値を返します。

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


as!強制フォームであることがわかっており、ダウンキャストを試み、結果を単一の複合アクションとして強制的にアンラップします。

ダウンキャストが常に成功することが確実な場合にのみ使用してください。この形式の演算子は、不適切なクラスタイプにダウンキャストしようとすると、ランタイムエラーをトリガーします

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


4

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

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

また、z2 = dict [2]を次のようにします。文字列// "Yo"(オプションではない)
Jay

0

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

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

詳細については、この関連質問をご覧ください


0

Swiftでこれらの演算子のパターンを覚えるのが最も簡単な場合があります。!「これはトラップする?可能性があります」を意味し、「これはnilである可能性があります」を示します。

参照:https : //developer.apple.com/swift/blog/?id=23


-1

私はSwiftの初心者であり、「オプション」について理解しているときに説明しようとするこの例を書いています。私が間違っている場合は修正してください。

ありがとう。


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

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

回答:(1)ここでプログラマーは、“obj.lName”文字列型オブジェクトが含まれていることを確信しています。したがって、その値をに与えるだけ“obj.lastName”です。

さて、プログラマが正しい手段であれば"obj.lName"文字列型オブジェクトであれば問題ありません。「obj.lastName」は同じ値に設定されます。

しかし、プログラマーが間違っている場合"obj.lName"は、文字列型オブジェクトではありません。つまり、「NSNumber」などの他の型オブジェクトが含まれています。クラッシュ(実行時エラー)。

(2)プログラマは、“obj.lName”文字列型オブジェクトまたはその他の型オブジェクトが含まれていることを確認していません。“obj.lastName”文字列型の場合は、その値を設定します。

さて、プログラマが正しい手段であれば“obj.lName”文字列型オブジェクトであれば問題ありません。“obj.lastName”同じ値に設定されます。

しかし、プログラマーが間違っている場合は、obj.lNameが文字列型オブジェクトではないことを意味します。つまり、他の型オブジェクトが含まれています"NSNumber"。その後“obj.lastName”、nil値に設定されます。だから、クラッシュなし(Happy :)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.