15

私はswiftLintを使い始めました。Swiftのベストプラクティスの1つは、強制的なキャストを避けることです。細胞のためのtableView、collectionViewを取り扱う場合しかし、私はそれをたくさん使用:これはベストプラクティスではない場合フォースキャストは本当に悪いですか?常にそれを避けるべきですか?

let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellID, forIndexPath: indexPath) as! MyOffersViewCell 

、これを処理するための正しい方法は何ですか?私はletとして使うことができると思いますが、空のセルを返す必要があるelse条件を意味しますか?それは受け入れられますか?

if let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellID, forIndexPath: indexPath) as? MyOffersViewCell { 
     // code 
} else { 
     // code 
} 
+1

を私が使用して言うと思います力アンラッピングは許容可能です。あなたがしていることを知っている限り。しかし、あなたの特定の状況では、オプションのアンラッピングを使う方が良いでしょう。 'dequeueReusableCellWithReuseIdentifier'によって返されたセルが' MyOffersViewCell'の型かどうかを調べることができます。もしそうなら、あなたが望むものを何でもしてください。そうでなければ 'UITableViewCell'を返してください。問題はありません。 –

答えて

28

この質問はおそらく意見基づいていますので、塩の粒で私の答えを取るが、私はその力のダウンキャストは常に悪いとは言わないでしょう。セマンティクスと、それが与えられた状況でどのように適用されるかを考慮する必要があります。

as! SomeClassは基本的に「これはSomeClassのインスタンスであることを保証します」と言います。それがSomeClassではないことが判明した場合、あなたが契約に違反したため例外がスローされます。

この契約を使用している状況と、強制ダウンキャストを使用していない場合は、適切な処置を検討する必要があります。例では

dequeueReusableCellWithIdentifierが、あなたはおそらく、細胞の再利用識別子とは何かを誤って設定しており、例外はあなたがその問題を見つけましょうあなたにMyOffersViewCellを与えるものではありません場合は、与えます。

条件付きダウンキャストを使用した場合、nilを取得して何とか処理する必要がありますか?メッセージを記録しますか?例外を投げる?これは確かに、あなたが開発中に見つけたい、回復不可能なエラーと何かを表しています。リリース後にこれを処理する必要はありません。あなたのコードは突然異なるタイプのセルを返すようになることはありません。フォースダウンキャストでコードがクラッシュするだけであれば、問題が発生した行を直接指します。

ここで、Webサービスから取得したJSONにアクセスする場合を考えてみましょう。あなたのコントロールを超えているWebサービスに変更がある可能性があるので、これをより上手く処理するといいかもしれません。あなたのアプリが機能することができないかもしれませんが、少なくとも、あなたは警告ではなく、単にクラッシュを表示することができます。

BAD - クラッシュJSONは配列

let someArray=myJSON as! NSArray 
... 

ベターではない場合 - 無効なJSONを扱います警告

guard let someArray=myJSON as? NSArray else { 
    // Display a UIAlertController telling the user to check for an updated app.. 
    return 
} 
+2

それはあなたの意見ですが、それでいいですが、個人的には、私が使用しているアプリが、ちょうどクラッシュよりも問題があることを私に知らせることを願っています。サーバが単なる壊れているのではなく、私の答えで示唆しているように、APIを一方的に予告なしに変更した第三者から提供された場合これは素晴らしいことではありませんが、起こる可能性があります。アプリがクラッシュログを送信せずに問題を報告できる方法は他にもあります。 – Paulw11

+2

SwiftLintを使用していて、単に違反を無音にしたい場合: let someArray =(myJSON as NSArray)! – ataranlen

5

「強制出演は」あなたは何をしているキャストすると元のためにその型であることを知ってとき、その場所を持っていると十分な。

また
myLabel = myView.viewWithTag(1) as! UILabel 

、より安全なオプションを使用することです:

たちはmyViewタグ1UILabelあるサブビューを持っていることを、私たちはUIViewUILabelへの安全性からキャストを下に先に行くと力することができます知っていると言いますガード。

後者は明らかに悪いケースを処理するので安全ですが、前者はより簡単です。だから実際には、それは将来的に変更されるかもしれない何か、あるいはあなたがアンラッピングしているものがその状況に投げかけたいものかどうかを確信していないかどうかを考慮して、正しい選択。あなたはそれが何か他のものがPaulw11の答えに加えて、ガード

6

を使用するかもしれないわずかなチャンスをtheresの場合、あなたはそれ以外のキャストを強制することができますになります正確に何を知っている場合は、このパターンは、完全に有効です。要約すると

安全で便利な、時には:

Songまたは Movieオブジェクト(メディアの両方のサブクラス)のどちらかを含めることができ Mediaインスタンスの配列、:

if myObject is String { 
    let myString = myObject as! String 
} 

は、Appleによって与えられた例を考えてみましょう

let mediaArray = [Media]() 

// (populate...) 

for media in mediaArray { 
    if media is Song { 
     let song = media as! Song 
     // use Song class's methods and properties on song... 
    } 
    else if media is Movie { 
     let movie = media as! Movie 
     // use Movie class's methods and properties on movie... 
    } 

EDIT:しばらくSwiftlintを使用した後、私は今(以下ケビンさんのコメント@に沿って)ゼロフォース・アンラッピングカルトへの総変換しています。 、

for media in mediaArray { 
    if let song = media as? Song { 
     // use Song class's methods and properties on song... 

    } else if let movie = media as? Movie { 
     // use Movie class's methods and properties on movie... 
    } 
} 

...以上をは、それぞれのタイプの[Song][Movie]配列をタイプ2(空)にmediaArrayをオンにするflatMap()を使用します。

だから、今日では私はこれを行うだろう。しかし、それは問題の範囲外です(force-unwrap)...

さらに、テーブルビューのセルをデキューしても、強制解除しません。デキューされたセルを適切なUITableViewCellサブクラスにキャストできない場合は、ストーリーボードに何か問題があることを意味します。したがって、復旧できる実行時条件ではありません(むしろ、検出と修正が必要な開発時エラー)私はfatalError()と結婚します。

+2

私はそれを強制的に鋳造するのではなく、アンラッピングするのが大好きです。 'もしmyString = myObjectを?文字列 'または'もしsong = mediaを?ソング{} else if movie = media as?映画 '。そのパターンは安全ですが、オプションのアンラッピングは強制的にアンラッピングすることなく行うことができます – Kevin

+1

私はそれがスタイル/好みの問題だと思っています。実際、それは私がいつもやっていることです(私は巨大な 'if/let' - ' guard/let/else'ファンです)。私はちょうどAppleのドキュメントからこの例を思い出しました... –

0

実際にオブジェクトが指定されたタイプであることが確実な場合は、ダウンキャストしても問題ありません。しかし、私は私の目にはより良いアプローチであるログでより意味のある結果を得るために、それらの例には、次のグローバル関数を使用します。

public func castSafely<T>(_ object: Any, expectedType: T.Type) -> T { 
    guard let typedObject = object as? T else { 
     fatalError("Expected object: \(object) to be of type: \(expectedType)") 
    } 
    return typedObject 
} 

使用例:

class AnalysisViewController: UIViewController { 

    var analysisView: AnalysisView { 
     return castSafely(self.view, expectedType: AnalysisView.self) 
    } 

    override func loadView() { 
     view = AnalysisView() 
    } 
} 
関連する問題