iOS デバイスで Unity アプリケーションを実行する際の一般的なクラッシュや、その他の問題の解決法を見つけるために役立つ情報を以下に紹介します。
ノート: トラブルシューティング後も問題が解決しない場合は、iOS のクラッシュバグレポートを参照してください。
この問題の一般的な原因について、その一部を以下のリストにまとめました。
List<int>、List<SomeStruct>、シリアル化されたスクリプトプロパティの List<SomeEnum> など。Xcode のデバッガーコンソールからの情報は、多くの場合、これらの問題の検出に役立ちます。デバッガーコンソールには、View > Debug Area > Activate Console からアクセスします。
このメッセージは通常、アプリケーションが NullReferenceException を受信したときに iOS デバイスに表示されます。障害が発生した場所を見つけるには、ネイティブスタックトレースを使用します。
ネイティブスタックトレースは、障害調査のための便利なツールですが、使用する際には多少の専門知識を必要とします。さらに、一般的には、ハードウェアメモリアクセスエラーの発生後には続行できません。ネイティブスタックトレースにアクセスするには、Xcode のデバッガーコンソールに bt all と入力します。出力されたスタックトレースには、エラーが発生した場所に関する情報が含まれている可能性があるため、それらを調べます。例えば、一般的なスタックトレースは次のようになります。
...
Thread 1 (thread 11523):
1. 0 0x006267d0 in m_OptionsMenu_Start ()
1. 1 0x002e4160 in wrapper_runtime_invoke_object_runtime_invoke_void__this___object_intptr_intptr_intptr ()
1. 2 0x00a1dd64 in mono_jit_runtime_invoke (method=0x18b63bc, obj=0x5d10cb0, params=0x0, exc=0x2fffdd34) at /Users/mantasp/work/unity/unity-mono/External/Mono/mono/mono/mini/mini.c:4487
1. 3 0x0088481c in MonoBehaviour::InvokeMethodOrCoroutineChecked ()
...
メインスレッドとなる “Thread 1” のスタックトレースを探します。スタックトレースの最初の行は、エラーが発生した場所を指しています。この例では、トレースは _OptionsMenu_ スクリプトの _Start_ メソッド内で NullReferenceException が発生したことを示しています。このメソッドの実装を調べることで、問題の原因を究明できる可能性があります。一般的に、NullReferenceExceptions は、初期化の順序について前提が誤っている場合に、_Start_ メソッドの内部で起きます。
場合によっては、スタックトレースの一部分のみがデバッガーコンソールに表示されます。 例:
Thread 1 (thread 11523):
1. 0 0x0062564c in start ()
このメッセージは、ネイティブのシンボルがアプリケーションのリリースビルドの際に取り除かれたことを示しています。さらに調査するには、以下のステップで完全なスタックトレースにアクセスします。
Program received signal: "0" などの警告メッセージが表示される場合があります。この警告メッセージは、多くの場合は致命的ではなく、iOS のメモリが不足していることを示しています。一般的には、メールのようなバックグラウンドプロセスでいくつかのメモリが解放され、アプリケーション実行が継続されます。ただし、アプリケーションが引き続きメモリを使用したり、メモリの追加を要求したりすると、iOS はそれ自体を含むアプリケーションの終了を開始します。Apple は安全なメモリ使用量についてのドキュメントを提供していませんが、使用量がデバイス RAM 全体の 50% 未満のアプリケーションでは、メモリ使用量に大きな問題が発生しないことが観察から示されています。
使用する主な指標は、アプリケーションの RAM 使用量です。アプリケーションのメモリ使用量は、以下の部分で構成されています。
| Component | 説明 |
|---|---|
| アプリケーションコード | OS は RAM にアプリケーションコードをロードし、保持する必要がありますが、実際には、必要な場合に一部が廃棄されることがあります。 |
| ネイティブヒープ | エンジンの状態とユーザーのアセットを RAM に格納するために、エンジンによって使用されます。 |
| マネージヒープ | C# オブジェクトを格納するために il2cpp ランタイムによって使用されます。 |
| Metal メモリプール | テクスチャ、フレームバッファ、コンパイルされたシェーダーを格納するために使用されます。 |
Xcode でアプリケーションメモリの使用状況を追跡できます。詳細は、Gathering information about memory use (Apple Developer) を参照してください。
メモリ使用量を低く抑えるには、以下の推奨事項を実施します。
空きメモリの量について OS に照会することは、アプリケーションのパフォーマンスを評価する効率的な方法のように思えるかもしれません。しかし、OS はダイナミックバッファとキャッシュを多く使用しているので、空きメモリの統計は信頼できない傾向があります。アプリケーションのメモリ消費を追跡し、特に新しいシーンをロードした後は、このメモリ消費量を主な指標とし、Xcode メモリツールと組み合わせて使用することを推奨します。
デバイスのログを調べて詳細を確認することを推奨します。このためには、以下のステップを実施してください。
クラッシュレポートも調べる必要があるかもしれません。詳細は、Acquiring crash reports and diagnostic logs (Apple Developer) を参照してください。
iOS アプリケーションによる最初のフレームの描画と入力の処理には、時間制限があります。アプリケーションはこの制限を超えると、SpringBoard によって強制終了されます。例えば、最初のシーンのサイズが大きすぎるアプリケーションで、この強制終了が発生する可能性があります。スプラッシュスクリーンを使用した、サイズの小さな初期シーンを作成し、数フレーム待ってから大きなシーンのロードに進むことをお勧めします。以下の例を使用できます。
IEnumerator Start() {
yield return new WaitForEndOfFrame();
// Do not forget using UnityEngine.SceneManagement directive
SceneManager.LoadScene("Test");
}
.NET 暗号化サービスは、マネージコードストリッピングと互換性がありません。これらのサービスはリフレクションに依存しますが、マネージコードストリッピングには静的コード分析が含まれています。ストリッピングプロセスは、カスタムの link.xml ファイルを Unity プロジェクトの Assets フォルダーに加えてカスタマイズできます。これにより、ストリッピングから除外するタイプと名前空間を指定できます。ストリッピングプロセスから System.Security.Crypography 名前空間を除外すると、この問題の解決に役立ちます。例えば、link.xml ファイルに以下を追加します。
<linker>
<assembly fullname="mscorlib">
<namespace fullname="System.Security.Cryptography" preserve="all"/>
</assembly>
</linker>
この問題は、前のセクションで説明した同じ方法で解決できます。または、スクリプトコードに特定のクラスへの余分な参照を追加することもできます。以下の例を使用して実施できます。
object obj = new MD5CryptoServiceProvider();
UI 内の一部の操作では、iOS がすぐにウィンドウを再描画することになります。特に一般的な例は、UIViewController を持つ UIView を、メインの UIWindow に追加する操作です。スクリプトからネイティブ関数を呼び出すと、Unity の PlayerLoop 内部でその呼び出しが発生するため、結果として PlayerLoop が再帰的に呼び出されることになります。これが発生すると、エラーメッセージ PlayerLoop called recursively! を受け取ることになります。このような場合、waitUntilDone を false に設定したうえでの performSelectorOnMainThread (Apple Developer) メソッドの使用を検討してください。これにより、Unity の PlayerLoop 呼び出しの間に実行すべき処理をスケジュールするように、iOS に通知されます。
この問題を診断するには、以下の推奨事項を実施してください。
54997 の 225.0.0.222 に マルチキャストやブロードキャストを行います。ネットワーク設定でこのトラフィックが許可されていることを確認してださい。プロファイラーはリモートデバイスのポート番号 55000 - 55511 の範囲に接続し、デバイスからプロファイラーデータをフェッチします。これらのポートは TCP アクセスのために開いている必要があります。
この問題は、ビルドされたコードが大きすぎて Xcode の制限に該当した場合に発生します。多くのスクリプトコードがある場合や、ビルドで大きな外部 .NET アセンブリを使用している場合に生じることがあります。Script Debugging ビルド設定を使用すると、各関数に追加の指示が作成されるため、この問題がさらに大きくなる可能性があります。
この問題に対処するには、Unity エディターで Edit > Project Settings > Player > iOS に移動し、以下のオプションを 1 つ以上試してください。
問題が解決しない場合は、ユーザースクリプトコードを複数のアセンブリに分割することをお勧めします。例えば、Plugins フォルダーを使用して、この場所のコードが別のアセンブリに追加されるときに、任意の分割コードを配置できます。また、特殊フォルダー名がスクリプトのコンパイルに与える影響については、特殊フォルダーとスクリプトのコンパイル順を参照してください。
Apple は Xcode バージョン 14.3 から、Destination Architecture オプションを導入しました。Destination Architecture では、ARM ベースの Mac で iOS シミュレーターを使用するために、Rosetta シミュレーターモードで Xcode を実行する必要がありません。
iOS シミュレーターを表示するには、以下のステップを行います。