iPhone 16シミュレータでのクラッシュ問題
最近、Macのアップデートが来ていたので、早速バージョンアップを実行。
そして、ついでにXcodeのバージョンも最新にしたんですよ。
ここまでは順調…と思っていたのですが。
いざ、iPhone 16のシミュレータを使ってみると、特定のシチュエーションでアプリが突然クラッシュ!「あれ、なんでだ?」と思って、少し状況を変えてみても…また落ちる。落ちる、落ちる…まるでiPhoneの中で何かが暴れているかのような勢い。
こりゃ一体、何が起こってるんだ?という感じである。
試しにiOS17.5に落としてみると落ちない。
さっきまでの状況が嘘のようだ。
原因調査
さて、ついに始まりました!
みんな大好きな「原因調査タイム」がやってきましたよ。
まずはクラッシュレポートをチェックしてみましょう。
そこにはこんなエラーメッセージが…
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Expected dequeued view
to be returned to the collection view in preparation for display.
When the collection view's data source is asked to provide a view
for a given index path, ensure that a single view is dequeued and
returned to the collection view. Avoid dequeuing views without
a request from the collection view.
ん?なんだこれ?よく分からんぞ…。
DeepLさんにお願いしてみました。
コレクションビューが必要とするビューを取り出す処理(デキュー)を適切に行う必要があり、ビューを取り出して返す操作が不正だったことが原因でクラッシュが発生しているということです。
うーん、でもいまいちピンと来ないので、次はデバッグだ!
「もしかしてここが怪しいんじゃないか?」と思ってブレイクポイントを設置。
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
すると…やっぱり、ここで落ちてる!そして、同じエラーメッセージが出現。
どうやら原因特定完了です。
さあ、次は修正作業!
修正編
問題の特定ができたので、あとは修正するだけ! というわけで、やっとこさゴールが見えてきました。
今回は、コレクションビューでビューを取り出す処理(dequeueReusableCell())を使って、セルをいろんなパターンに分けて作成していました。最初に複数のセルを一度に宣言しておいて、あとはreturnで返すという方法です。こんな感じで…
let cellA:collectionViewCellA = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCellA", for: indexPath) as! collectionViewCellA
let cellB:collectionViewCellB = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCellB", for: indexPath) as! collectionViewCellB
swtich indexPath.section{
case 0:
return cellA
case 1:
return cellB
default:
return cellA
}
ところが、これがちょっと面倒なことになってしまったのが、Xcode 16 + iOS 18 の環境で動かした時。どうやら「必要ないセルをデキューするのはよろしくないよ!」という警告が出るようで、最終的には「1つだけ取り出してね!」というお達しが。
必要なセルを必要な時にだけ取り出すようこんな風に修正しました。
swtich indexPath.section{
case 0:
let cellA:collectionViewCellA = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCellA", for: indexPath) as! collectionViewCellA
return cellA
case 1:
let cellB:collectionViewCellB = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCellB", for: indexPath) as! collectionViewCellB
return cellB
default:
let cellA:collectionViewCellA = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCellA", for: indexPath) as! collectionViewCellA
return cellA
}
今のところ問題なく動作しているので、修正としてはこれで合ってるはず。作り方としては少し独特だったので、情報を探すのに時間がかかりましたが、なんとか解決に至ってホッとしています。最終的にうまく修正できたので、結果オーライですね!
最後に
という事で今回はChatGPTちゃんの協力により、文章を面白く読みやすいように作り直してもらってみました。変な部分もありますが、意外と文章打てるんだなって印象でしたがいかがでしたか?楽しんで読んでもらえたなら何よりです。