記事

XCodeアップグレードで必須チェック|Releaseビルドの幽霊クラッシュ対策とロジック確認

Releaseビルド時のみ発生する幽霊クラッシュやロジック問題に悩む開発者向けに、XCodeアップグレード時の効果的な検証手法を解説。Debugでは見えない不具合を早期発見し、安定したリリースを実現します。

XCodeアップグレードで必須チェック|Releaseビルドの幽霊クラッシュ対策とロジック確認

本記事は AI による翻訳をもとに作成されています。表現が不自然な箇所がありましたら、ぜひコメントでお知らせください。

記事一覧


[通霊ノート] XCode アップグレード時に必ずテストすべきこと…

Build Configuration Release(正式版、オンライン版)でのみ発生する原因不明のクラッシュやプログラムロジックの問題があり、Debugでは問題が全く発生しない場合。

Photo by [Tommaso Pecchioli](https://unsplash.com/@pecchio?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash){:target="_blank"}

Photo by Tommaso Pecchioli

TL;DR

新しい XCode でプロジェクトをビルド・リリースする前に、単に Build & Run してレイアウト崩れや異常がないか確認するだけでなく、以下も必ず試してください

  1. Appターゲット

  2. Build Settings を選択してください

  3. Optimization Level を検索する

  4. Optimization Level セクションを見つける

  5. Debug 環境も Release と同じ値に設定する(例:Fastest, Smallest [-Os]

  6. Build & Run して異常がないか確認する

直接に Testflight にパッケージをアップロードしてテストしないのは、問題が発生した際にすぐにブレークポイントで原因を特定できるようにするためです。

もしユーザーから Release(正式版、オンライン版)で報告された問題(クラッシュや異常動作)があり、開発者がローカルで再現できない場合は、この設定を変更してローカルで試してみるのも手です。

発生する可能性のある問題

  • コードは正しいように見えるが、結果が異常になる

  • プログラム上はクラッシュしないはずの箇所でクラッシュが発生する

上記は Debug 環境の Optimization Level = None [-O0] では正常ですが、Optimization Level = Fastest, Smallest [-Os]、つまり Release の設定値でのみ発生します。

解決方法

問題がある場合、多くは開発者のせいではありません;XCode の最適化バグが原因です。このバージョンの XCode でどうしてもビルドする必要がある場合は、まず自分でプログラムの回避策を調整し、新しいバージョンの XCode が出るのを待って正常かどうかを確認してください。

Release を直接 None に変更することは推奨しません。なぜなら、他の問題がさらに発生する可能性があるからです。

お話の時間

以下はここ数年の仕事で実際に遭遇した問題の例です。

ストーリー 1 — アプリが評価依頼のポップアップを繰り返し表示する

私たちのアプリには以前、アプリ起動時に「ユーザーにアプリストアで評価をお願いする」機能がありました。ルールとしては3回表示した後は表示しないようになっていましたが、多くのユーザーから毎回アプリを開くたびに表示され続けると報告がありました。ずっと続いていて、何度も何度も表示されて非常に迷惑だという声がありました。

しかし、コードを確認し、ローカルで Build & Run をシミュレーターや実機で行っても問題はなく、さまざまなエッジケースも試しましたが再現できませんでした。UIテストも作成し、何千回もルートを繰り返し、データをクリアして再試行しましたが、問題は発生しませんでした。

あの時は夜中の3時過ぎまで悩み抜き、途方に暮れて原因が全くわからず、目的もなくプロジェクト設定を見て回っていました。すると突然ひらめいて、Build Settings をすべて Release の値に変更してみたところ、問題が Optimization Level = Fastest, Smallest [-Os] にあることが判明し、問題箇所を特定できました。

擬似コード

1
2
3
4
5
6
7
8
9
10
11
12
var invitedTimes = 0 // UserDefaultsから読み込み;更新後に保存される
func requestAppStoreReviewIfNeeded() {
  defer {
    invitedTimes += 1 // 今のところ動作するが、予期しない副作用がある可能性がある
  }

  guard invitedTimes < 3 else {
    return
  }
  
  self.present(AppStoreReviewRequestAlert())
}

このコードは前任者が開発したもので、コード上は副作用がありますが、ロジックには問題がなく、正常にコンパイルでき、以前のバージョンでも実行に問題はありませんでした。

しかし、Optimization Level = Fastest, Smallest [-Os] に設定してブレークポイントで値をプリントすると異常が発覚しました。invitedTimes += 1 の後に、値が突然 -24760045646797946 のような非常に大きな負の数に変わり、そのためユーザーには毎回評価の招待が表示されてしまいます。

当時まずこの defer の書き方を直接変更したところ、それ以降ユーザーから同様の問題報告はありませんでした。その後、後続の XCode バージョンで同じ書き方と Optimization Level = Fastest, Smallest [-Os] をテストしたところ、正常に動作しました。

ストーリー 2 — あるページで直接クラッシュする

Release(Testflight)版の内測で、あるページ(WebView)をクリックすると必ずクラッシュする問題が発生しました。しかし、エンジニアがシミュレーターや実機で Build & Run しても問題はありませんでした。問題点を推測してはログを埋め込んだり修正したバージョンを Testflight にアップしてテストする作業が非常に苦痛で時間がかかりました。その時、以前の恐怖がよみがえり、すぐに同僚にローカルの設定を Optimization Level = Fastest, Smallest [-Os] に変更してもらい、Build & Run をしたところ、見事にローカルでクラッシュ問題を再現できました。

主な問題は、私たちが独自にカスタマイズした WebView の Obj-c コード内の変数が、Optimization Level = Fastest, Smallest [-Os] のときに null になることで、原因は不明です。とりあえず判定を強化して保護しています。以前のバージョンでは正常に動作していたため、新しい XCode がリリースされるまで様子を見るしかありません。

まとめ

実はこの罠には二度も引っかかっており、他にも覚えていないものがありますが、とにかく心得を残しておきます:

  1. 新しい XCode バージョンで初めてビルドしてリリースする際は、必ずこれをテストしてください

  2. 問題は Release(正式版、オンライン版)でのみ発生し、基本的にこの問題です。設定を変更してローカルで再現できるか確認してください。

ご質問やご意見がありましたら、こちらからご連絡ください

Post MediumからZMediumToMarkdownを使って変換しました。


🍺 Buy me a beer on PayPal

👉👉👉 Follow Me On Medium! (1,053+ Followers) 👈👈👈

本記事は Medium にて初公開されました(こちらからオリジナル版を確認)。ZMediumToMarkdown による自動変換・同期技術を使用しています。

Improve this page on Github.

本記事は著者により CC BY 4.0 に基づき公開されています。