はじめに
どうもどうまずです。
本当に書いちゃった怖いソースをお見せしようと思います。 特に今回はクラッシュするレベルのコードです。
強制ダウンキャスト
まずはこちらのソースをご覧頂きたい。 (ちなみに、コンパイルエラーにはなりません。)
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { return collectionView.dequeueReusableCell(withReuseIdentifier: DetailCellIdentifier.memo.rawValue, for: indexPath) as! DetailMemoCell }
おわかり頂けただろうか?
一見なんの変哲もない、collectionViewのセルの内容を返しているだけのソースに見えます。
しかし、「as!」と記載しています。
そう、つまりこれ、
強制ダウンキャストしているんです!
万が一、ダウンキャストが失敗した場合、
アプリが落ちます!
なんとも恐ろしいソースではないでしょうか!
collectionViewやtableViewのサンプルとかみると、結構高い確率で強制ダウンキャストしているので、私も右に倣えでやってしまいましたが、要注意しないといけませんね。 (サンプルを記載している方は、理解しやすいように余計な処理書いていないだけだと思います。)
テスト中ならまだしも、リリースしてからクラッシュはどうしても避けたいところですよね。
修正方法
nilが入らないようにするのは大前提ですが、想定外のことが起きるのは世の常。 万が一nilが入ってしまった場合に、落ちないようにしましょう。
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { if let memoCell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailCellIdentifier.memo.rawValue, for: indexPath) as? DetailMemoCell { return memoCell } else { return UICollectionViewCell() } }
重要なのは
if let memoCell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailCellIdentifier.memo.rawValue, for: indexPath) as? DetailMemoCell
です。 右辺の結果がnilでなければ、左辺に代入してネストした処理が実行されます。 この例では更に「as!」を「as?」に変更することでダウンキャストを行えるかも一緒にチェック(ダウンキャスト出来なければ nilが返るから、elseに入る)をおこなっています。
これで万が一の事態でもアプリが落ちるようなことはありませんね。
asっておまじないの一種と思ってた・・・
類似事例:強制アンラップ
似たような事象で強制アンラップも怖いです。
簡単な事例を一つ。
var test:String? test = test2 print(test!)
test2という変数に何が入っているかわかないですが、仮にtest2がnilだった場合、「print(test!)」でクラッシュです。
修正方法
指摘されたのは、「強制アンラップを利用する時は絶対nilではないという自信がある場合のみ」です。 理由は下手するとクラッシュする恐怖があるためです。
そのため、アンラップする必要がある場合、強制ダウンキャストと同じく、「if let」を利用しましょう!
if let test = test2 { print(test) } else { print("nilだよ") }
test2がnilでなければ左辺に代入され、ネスト内の処理が行われます。 ちなみに、testはアンラップされた形式になっています。
つまり
・オプショナルに慣れるまでは強制ダウンキャストや強制アンラップは避けましょう
・どうしても使う場合は、「if let」でnilか調査してから使いましょう
この2点を注意しましょう。
…いや、注意します。
注意
レビューアの観点によって、諸説あります。