web-dev-qa-db-ja.com

強制ダウンキャストをオプションとして扱うと「nil」は生成されません

私はSwiftをいじってみましたが、ディクショナリに挿入するオブジェクトをダウンキャストすると、奇妙な警告が表示されます:_Treating a forced downcast to 'String' as optional will never produce 'nil'_。as __as?_を使用すると、警告は表示されなくなります。

_func test() -> AnyObject! {
  return "Hi!"
}

var dict = Dictionary<String,String>()
dict["test"]=test() as String
_

Appleのドキュメントには次のように書かれています

ダウンキャストは失敗する可能性があるため、型キャスト演算子には2つの異なる形式があります。オプションの形式_as?_は、ダウンキャストしようとしている型のオプションの値を返します。強制フォームasは、ダウンキャストを試み、結果を単一の複合アクションとして強制的にアンラップします。

ここで、なぜasの代わりに_as?_を使用するのが正しいのか、はっきりしません。一部のテストでは、test()を変更してStringではなくIntを返すようにした場合、asを使い続けるとコードがエラーで終了することがわかります。 _as?_の使用に切り替えると、コードは通常どおり実行を続け、そのステートメントをスキップします(dictは空のままです)。しかし、なぜこれが望ましいのかはわかりません。私の意見では、私はむしろプログラムをエラーで終了し、キャストが失敗したことを知らせてから、単に誤ったステートメントを無視して実行を続けます。

ドキュメントによると、「ダウンキャストが常に成功することが確実である場合にのみ」強制フォームを使用する必要があります。この場合、test()は文字列のみを返すことができるため、ダウンキャストは常に成功するので、これは強制的な形式のダウンキャストに最適な状況であると思います。では、コンパイラが警告を表示するのはなぜですか?

34
Pamelloes

最後の行を詳しく見て、それを分解して何が起こっているかを見てみましょう。

_let temporaryAnyObject = test()
let temporaryString = temporaryAnyObject as String
dict["test"] = temporaryString
_

エラーは2行目にあり、temporaryAnyObjectStringであることを強制するようコンパイラーに指示しています。コードは引き続きコンパイルされます(警告をエラーとして扱わないと想定)が、temporaryAnyObjectが実際にStringでない場合はクラッシュします。

_?_なしでasが機能する方法は、基本的には「今後、この式の結果をその型として扱います。結果が実際にその型である場合、そうでない場合、一貫性がなくなり、実行できなくなります。 。

_as?_の動作方法(_?_を使用)は、「今後、この式の結果をその型として扱います。結果が実際にその型の場合、そうでない場合、この式の結果はnilになります。 。

したがって、上記の分解例では、test()Stringを返す場合、asダウンキャストは成功し、temporaryStringStringになりました。 test()Stringを返さないが、IntまたはStringからサブクラス化されていない何かを言うと、asは失敗し、コードは実行を継続できなくなります。

これは、開発者が完全に制御できるように、オプションの_?_インジケーターを配置しないことにより、システムにこのように動作するように指示したためです。 asコマンドは、オプションの動作を許容せず、そのダウンキャストが機能する必要があることを具体的に意味します。

_?_を指定した場合、temporaryStringnilになり、3行目は "test"キー/値のペアをディクショナリから単純に削除します。

これは奇妙に思えるかもしれませんが、これは、デフォルトですべてをオプションとして扱い、独自のチェックとアサートを配置することに依存しているObj-Cなどの多くの言語のデフォルトの動作とは逆であるためです。

編集-Swift 2更新

Swift 2なので、強制的に失敗する可能性のあるダウンキャスト演算子asが削除され、Swiftierである_as!_に置き換えられました。動作は同じです。

46
Ryan

この警告は2つの角度から解決できます。 1.返される値2.返される値が期待されるタイプもう1つの答えは、第1の角度についてです。私は第二の角度について話している

これは、オプションに対して強制アンラップおよびキャスト値を返すためです。コンパイラは「本当にすべてのオプションを強制的にキャストしたい場合は、期待される戻りパラメータをnon-optional

例えばあなたが書いた場合

func returnSomething<T> -> T?{ // I'm an optional, I can handle nils SAFELY and won't crash.

return UIViewController as! T // will never return a safe nil, will just CRASH

}

基本的に、あなたは自分自身(そしてコンパイラー)にnilsを安全に処理したいと言いましたが、次の行で、いや、そうではありません!!!

コンパイラは警告を出します:

強制ダウンキャストをオプションとして「T」に処理しても、「nil」は生成されません。

代わりに、?

func returnSomething<T>() -> T{ // I can't handle nils

    return UIViewController() as! T // I will crash on nils
}

そうは言っても、おそらく最善の方法は、フォースキャストを使用せず、ただ実行することです。

func returnSomething<T>() -> T?{ // I can handle nils

    return UIViewController() as? T // I won't crash on nils
}
5
Honey

数年前にこの警告についてバグが開いているようです... https://bugs.Swift.org/browse/SR-4209 ですから、何が明らかかという状況でも表示されますあなたがやっていると正しい。

0
Renetik