Version: 2017.1
Unity iOS が現在サポートしていない機能
iOS のクラッシュバグのレポート方法

iOS デバイスのトラブルシューティング

iOS では、ゲームが Unity エディター上では完璧に動作するのに、その後、実際のデバイス上で動作しない、起動さえしないなどの状況が発生する場合があります。 問題は、多くの場合、コードやコンテンツの品質に関連してます。このセクションでは、もっとも一般的なシナリオについて説明します。

ゲームがしばらくして応答を停止し、Xcode がステータスバーに interrupted (中断) と表示する

これが起こる理由はいくつかあり、よくある原因は次のとおりです。

  1. 初期化されていない変数を使うなどのスクリプトエラー。
  2. サードパーティの Thumb (code) でコンパイルしたネイティブライブラリ使うこと。このようなライブラリは、iOS SDK のリンカーの既知の問題を誘発し、ランダムなクラッシュを引き起こす可能性があります。
  3. シリアライズ可能なスクリプトプロパティーのパラメーターとして(例 List<int>、List<SomeStruct>、List<SomeEnum> など) 値型と一緒にジェネリック型を使うこと。
  4. マネージコードストリッピングが有効になっているときリフレクションを使うこと。
  5. ネイティブプラグインインターフェースのエラー (マネージコードメソッドのシグネチャが、ネイティブコード関数のシグネチャと一致していない)。 Xcode のデバッガーコンソールからの情報は、多くの場合、これらの問題を検出するのに役立ちます (Xcode メニュー: View > Debug Area > Activate Console)。

Xcode コンソールに「Program received signal SIGBUS」または「EXC_BAD_ACCESS」エラーが表示される

このメッセージは通常、アプリケーションが NullReferenceException を受信したとき、iOS デバイスに表示されます。障害が発生した場所を把握するには 2 つの方法があります。

マネージドスタックトレース

Unity には、NullReferenceException のソフトウェアベースの処理を備えています。AOT コンパイラーは、変数がオブジェクトにアクセスするごとに null 参照を簡単にチェックします。この機能はスクリプトのパフォーマンスに影響するため、開発ビルドだけでに有効になっています (Build Settings ウィンドウの Script Debugging オプションを有効にします)。 すべてが正しく行われて、実際に障害が .NET コードで発生する場合は、EXC BAD ACCESS は表示されません。代わりに、.NET 例外が Xcode のコンソールに出力されます (あるいは、コードが単に catch 文でそれを処理します)。典型的な出力は以下のようになります。

Unhandled Exception: System.NullReferenceException: A null value was found where an object instance was required.
  at DayController+$handleTimeOfDay$121+$.MoveNext () [0x0035a] in DayController.js:122

これは、障害が DayController クラスのコルーチンとして動作している handleTimeOfDaym メソッドで起こったことを示しています。それがスクリプトコードであれば、通常、正確な行番号を通知します (例えば、DayController.js:122)。問題のある行は、以下のようなものが考えられます。

 Instantiate(_imgwww.assetBundle.mainAsset);

これは例えば、スクリプトが最初にそれが正しくダウンロードされたことを確認せずにアセットバンドルにアクセスした場合に発生する可能性があります。

ネイティブスタックトレース

ネイティブスタックトレースは、障害調査のためのより強力なツールですが、使用する際、いくつかの専門知識を必要とします。さらに一般的にネイティブエラー(ハードウェアメモリアクセス)が起きた後には続行できません。ネイティブスタックトレースを取得するには、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 のスタックトレースを見つける必要があり、それがメインスレッドです。スタックトレースの最初の行は、エラーが発生した場所を指しています。この例では、トレースは NullReferenceException が OptionsMenu スクリプトの Start メソッド内で起こったことを示しています。このメソッドの実装を注意深く見ると、問題の原因が明らかになるでしょう。一般的に、NullReferenceExceptions は、 Start メソッドの内部で初期化の順序について間違った前提を行った際に起きます。 場合によってはスタックトレースの一部分のみがデバッガーコンソールに表示されます。

Thread 1 (thread 11523): 

1. 0 0x0062564c in start ()

これはネイティブのシンボルがアプリケーションのリリースビルドの際に取り除かれたことを示しています。フルスタックトレースは、以下の手順で得ることができます。

  • デバイスからアプリケーションを削除します。
  • すべてのターゲットをクリーンアップします。
  • ビルドおよび実行します。
  • スタックトレースを上記の手順どおりに取得します。

外部ライブラリが Unity iOS アプリケーションにリンクされているとき EXC BAD ACCESS が発生する

通常、外部ライブラリは、ARM Thumb 命令セットでコンパイルされたときに生成されます。現在、このようなライブラリは Unity と互換性がありません。Thumb 命令なしでライブラリを再コンパイルすることで簡単に問題を解決することができます。Xcode プロジェクトのライブラリからは以下の手順でのこれを行います。

  • Xcode でメニューから View > Navigators > Show Project Navigator を選びます。
  • Unity-iPhone プロジェクトを選択し、Build Settings タブを有効にします。
  • 検索フィールドで Other C Flags と入力します。
  • -mno-thumb フラグをそこに追加してライブラリをリビルドします。

ライブラリのソースが利用できない場合は、Thumb コードではないライブラリのバージョンをサプライヤーに依頼する必要があります。

Xcode のコンソールに「WARNING -> applicationDidReceiveMemoryWarning()」が表示され、その後すぐにアプリケーションがクラッシュする

(時には、Program received signal: “0” のようなメッセージが表示される場合があります。) この警告メッセージは、多くの場合、致命的なものではなく、単に iOS のメモリが少なくなって、アプリケーションにいくつかのメモリを解放するを求めていることを示しています。一般的に、メールのようなバックグラウンドプロセスは、いくつかのメモリを解放し、アプリケーションを実行し続けることができます。ただし、アプリケーションがメモリを引き続き使用、またはそれ以上を求めた場合、iOS は最終的にアプリケーション達とそれらのいずれかを終了させる可能性があります。Apple はメモリ使用量がいくらあれば安全であるかを明文化していませんが、経験からデバイスの全 RAM50 %未満(第 2 世代 iPad ではおよそ 200 〜 256MB )をアプリケーションが使用している場合、メモリ使用量について大きな問題ありません。 注意するべき指標は、アプリケーションがどのくらいの RAM 使用するかです。あなたのアプリケーションのメモリ使用サイズは 3 つの主要なコンポーネントで構成されています。

  • アプリケーションコード( OS が RAM にアプリケーション·コードを読み込み、保持する必要がありますが、実際には必要に応じて一部が廃棄される場合があります)
  • ネイティブヒープ( state、asset 等を RAM に保存するためにエンジンによって用意される)
  • マネージヒープ( C #または JavaScript オブジェクトを維持するためにあなたの Mono ランタイムで使用)
  • GLES ドライバメモリプール:テクスチャ、フレームバッファ、コンパイルされたシェーダーなど アプリケーションのメモリ使用量は Xcode Instruments tools : Activity MonitorObject Allocations および VM Tracker でトラッキングできます。Xcode Run menu の Product > Profile から実行し、特定のツールを選択します。Activity Monitor ツールで、アプリケーションで使用する RAM の総量と Real memory を含むすべてのプロセスの統計情報を表示します。注意: OS やデバイスのハードウェアバージョンの組み合わせによりメモリ使用量の数値に著しく影響を与える可能性があるため、別のデバイス上で取得した数値を比較する際には注意する必要があります。

注意: ビルトインプロファイラーでのパフォーマンス測定は、.NET スクリプトによって割り当てられたヒープのみを表示します。総メモリ使用量は、上記のように Xcode Instruments を介して決定できます。この図では、アプリケーションバイナリの一部、いくつかの標準フレームワークバッファ、Unity エンジン内部ステートバッファ、.NET ランタイムヒープ(内部プロファイラーによって表示される番号)、GLES ドライバヒープ、いくつかの他のさまざまなものを含みます。

他のツールはアプリケーションによって行われた割り当てをすべて表示し、ネイティブヒープとマネージドヒープの両方の統計を含みます。(アプリケーションの現在の状態を取得するために、 Created and still living ボックスを忘れずにオンにしてください)。重要な統計は、 Net bytes 値です。

メモリ使用を抑えるために:

  • iOS の強力なストリッピングオプション( Advanced ライセンス機能)を使用してアプリケーションのバイナリサイズを小さくし、異なる .NET ライブラリからの不要な依存関係を避けるようにします。詳細については、プレイヤー設定ビルドした iOS プレイヤーのサイズ最適化のマニュアルページを参照してください。
  • コンテンツのサイズを小さくしてください。テクスチャに PVRTC 圧縮を使用し、ローポリゴンモデルを使います。詳細については、ファイルサイズの削減のマニュアルページを参照してください。
  • スクリプトで必要以上にメモリを割り当てないでください。Mono のヒープサイズと使用状況を ビルトインプロファイラーでのパフォーマンス測定で追跡します。
  • 注意: Unity3.0 以降、シーンのロードの実装は大幅に変更され、現在はすべてのシーンアセットがプリロードされています。この結果、ゲームオブジェクトをインスタンスする際、乱れることがあります。ゲームプレイ中にアセットのロードとアンロードをよりきめ細かく制御する必要がある場合は、Resources.LoadObject.Destroyを使用してください。

空きメモリの量についての OS に照会することは、アプリケーションのパフォーマンスを評価するためのよいアイデアのように思えるかもしれません。OS はダイナミックバッファとキャッシュを多く使用してるので、空きメモリの統計は信頼できない可能性が高いです。唯一信頼できるアプローチは、アプリケーションのメモリ消費量を追跡し、これを主な指標として使うことです。特に新しいレベルを読み込んだとき、上記のツールでどのようにグラフが時間の経過とともに変化するかに注意を払います。

ゲームが Xcode から起動したときには正しく実行するが、デバイス上で手動で起動すると最初のレベルの読み込み中にクラッシュする

これにはいくつかの理由が考えられます。より多く詳細を確認するために、デバイスのログをチェックする必要があります。Mac にデバイスを接続し、Xcode を起動してメニューから Window > Organizer を選択します。Organizer の左ツールバーで使っているデバイスを選択し、“Console” タブをクリックして慎重に最新のメッセージを確認します。さらにクラッシュレポートを調べる必要があるかもしれません。クラッシュレポートを取得する方法は以下にあります。 http://developer.apple.com/iphone/library/technotes/tn2008/tn2151.html

Xcode Organizer コンソールのメッセージに「killed by SpringBoard」が含まれる

貧弱なドキュメントには iOS アプリケーションの最初のフレームのレンダリングと入力処理に対しての時間制限があります。アプリケーションがこの制限を超えると SpringBoard に強制終了されます。これは、例えばアプリケーションの最初のシーンが大きすぎると起こるかもしれません。この問題を避けるには、スプラッシュスクリーンを表示するだけの小さな初期のシーンを作成し、 yield で 1 、 2 フレーム待ってから、実際のシーンを読み込むことをお勧めします。これは以下のような簡単なコードで実行できます。

function Start() {
    yield;
    Application.LoadLevel("Test");
}

Type.GetProperty() / Type.GetValue() がデバイスでクラッシュを引き起こすとき

今のところ、 Type.GetProperty()Type.GetValue() は、 .NET 2.0 Subset プロファイルでのみサポートされています。プレイヤー設定 での .NET API の互換性レベルを選択することができます。

注意: Type.GetProperty()Type.GetValue() は、マネージコードストリッピングと互換性がない可能性があり、除外する必要があるかもしれません(これを達成するためにストリッピング処理中にカスタムの非ストリッピングタイプのリストを提供して除外することができます)。より詳しいことは ビルドした iOS プレイヤーのサイズ最適化を参照してください。

ゲームがエラーメッセージ 「ExecutionEngineException: Attempting to JIT compile method ‘SometType`1<SomeValueType>:.ctor ()’ while running with –aot-only.」 を表示してクラッシュする

iOS 用の Mono.NET 実装は、AOT (ネイティブコードへ ahead of time compilation:事前コンパイル)に基づいており、制約があります。それは明示的に他のコードで使用されているジェネリック型のメソッド(バリュータイプがジェネリックパラメーターとして使用される場合で)のみをコンパイルします。このような方法が reflection または、ネイティブコード(すなわち、シリアライゼーションシステム)だけを経由して使われているとき AOT コンパイル中にスキップされます。AOT コンパイラは、スクリプトコードのどこかにダミーメソッドを追加することで、コードを include すると指示できます。これで足りないメソッドを参照できるので、それらを事前にコンパイルできます。

void _unusedMethod() {
    var tmp = new SomeType<SomeValueType>();
}

注意: バリュータイプは基本的な enums と structs です。

System.Security.Cryptography とマネージコードリッピングを使った組み合わせを使用したとき、さまざまなクラッシュがデバイス上で発生

.NET の暗号化サービスは、reflection に大きく依存し、これは静的コード分析を伴うため、マネージコードは、ストリッピングと互換性がありません。クラッシュが、ストリッピングプロセスから >System.Security.Crypography 名前空間の全体を取り除くことで、簡単に解決されることもあります。

ストリッピングプロセスは、Unity プロジェクトの Assets フォルダーにカスタム link.xml ファイルを追加することでカスタマイズすることができます。これでストリッピングから取り除く型と名前空間を指定します。詳細は ビルドした iOS プレイヤーのサイズ最適化を参照してください。

link.xml

<linker>
       <assembly fullname="mscorlib">
               <namespace fullname="System.Security.Cryptography" preserve="all"/>
       </assembly>
</linker>

マネージコードストリッピングで System.Security.Cryptography.MD5 を使ってアプリケーションがクラッシュする

上記のアドバイスを検討するか、スクリプトコード、特定のクラスに、参照を追加することでこの問題を回避できるかもしれません。

object obj = new MD5CryptoServiceProvider();

“Ran out of trampolines of type 0/1/2” ランタイムエラー

通常、再帰的なジェネリックを多く使用する場合に、このエラーは発生します。より多くの type0、type1、type2 トランポリンを割り当てるよう AOT コンパイラへ指示できます。追加の AOT コンパイラのコマンドラインオプションは プレイヤー設定の “Other Settings” セクションで指定できます。type 1 トランポリンの場合、 nrgctx-trampolines=ABCD を指定、つまり ABCD が必要な新しいトランポリンの数(すなわち 4096 )を指定します。type2 トランポリンは nimt-trampolines=ABCD を指定し、type0 トランポリンは ntrampolines=ABCD を指定します。

Xcode をアップデートした後、Unity iOS ランタイムが以下のメッセージを表示して失敗する 「You are using Unity iPhone Basic. You are not allowed to remove the Unity splash screen from your game (あなたは Unity iPhone Basic を使用しています。あなたのゲームから Unity スプラッシュ画面を削除することはできません)」

最新の Xcode リリースには PNG 圧縮と最適化ツールに変更を加えたものがあります。これらの変更は、スプラッシュスクリーン変更とき Unity の iOS 実行時チェックで誤った判定をする場合があります。このような問題が発生した場合は、Unity を最新の公開された利用可能なバージョンにアップグレードしてみてください。それで解決しない場合は、以下の回避策を試してください。

  • Unity からビルドするとき(それを追加するのではなく) Xcode プロジェクトをスクラッチから作ってビルドします。

  • すでにデバイスにインストールされているプロジェクトを削除します。

  • Xcode でプロジェクトをクリーンします。( Product -> Clean )

  • Xcode の Derived Data folder をクリアします。( Xcode -> Preferences -> Locations ) これでもまだ解決しない場合は Xcode で PNG の再圧縮を無効にしてみてください。

  • Xcode プロジェクトを開きます。

  • そこで“Unity-iPhone” プロジェクトを選択します。

  • そこで“Build Settings”タブを選択します。

  • “Compress PNG files”を見つけて、NO に設定します。

App Store への提出が失敗する。メッセージは「iPhone/iPod Touch: application executable is missing a required architecture. At least one of the following architecture(s) must be present: armv6」 (アプリケーション実行可能ファイルに必要なアーキテクチャではありません。少なくとも以下のアーキテクチャ必要が必要です。:armv6)

以前 armv6 サポートを受けて提出された既存のアプリケーションを更新するときには、そのようなメッセージが表示されることがあます。Unity4.x と Xcode の 4.5 は armv6 のプラットフォームをもうサポートしていません。提出問題を解決するために、Unity Player SettingsTarget OS Version4.3 以上に設定します。

WWW のダウンロードが Unity Editor と Android 上では正常に動作するが、iOS 上ではうまくいかない

よくある間違いは、WWW のダウンロードが常に別のスレッドで起きていることを前提にしていることです。一部のプラットフォームでは正しいかもしれませんが、当たり前のこととするべきではありません。WWW の状態を追跡するための最良の方法は、yield ステートメントを使用するか、または Update メソッド でステータスをチェックするかのどちらかです。while ループはビジーになるため。使うのは no です。

スクリプトからコールしたネイティブ関数を経由して Cocoa を使用した際に「PlayerLoop called recursively!(PlayerLoop が再帰的に呼ばれてます)」エラーが発生する

UI のいくつかの操作は iOS がすぐにウィンドウを再描画させます(もっとも一般的な例は、メインの UIWindow へ UIViewController と UIView を追加する場合です)。スクリプトからネイティブ関数を呼び出した場合、それは Unity の PlayerLoop 内部で発生し、結果的に PlayerLoop で再帰的に呼び出されることになります。その場合、waitUntilDone を false に設定し、performSelectorOnMainThreadメソッドの使用を検討する必要があります。Unity の PlayerLoop の呼び出しの間に実行する処理のスケジュールを iOS に通知します。

プロファイラーやデバッガーで iOS デバイス上でのゲームの動作を見ることができない

  • 開発ビルドを構築していることを確認し、(必要に応じて)“Enable Script Debugging”と“Autoconnect profiler”ボックスを設定してください。
  • デバイス上で動作するアプリケーションは、225.0.0.222 の UDP ポート 54997 に multicast/broadcast を行います。お使いのネットワーク設定がこのトラフィックを許可していることを確認してださい。その後、プロファイラーはデバイスからプロファイラーデータをフェッチするポート番号 55000 から 55511 の範囲リモートデバイスに接続を行います。これらのポートは UDP アクセスのために開いておく必要があります。

DLL が見つからない

アプリケーションはエディター上では実行が [OK] なのですが、iOS プロジェクトでエラーが出る場合、行方不明の DLL(例 I18N.dll、I19N.West.dll)によって引き起こされているかもしれません。この場合、それらの DLL を Unity.app 内からプロジェクトの Assets/Plugins フォルダーにコピーしてみてください。Unity アプリケーション内の DLL の場所は次のとおりです。 Unity.app/Contents/Frameworks/Mono/lib/mono/unity その後、ビルドが最適化されたとき、DLL クラスが削除されていないことを確認するために、プロジェクトのストリッピングレベルをチェックする必要があります。iOS ストリッピングレベルの詳細については、ビルドした iOS プレイヤーのサイズ最適化を参照してください。

–aot-only で実行するときに、Xcode デバッガーコンソールが以下を出力する: ExecutionEngineException: Attempting to JIT compile method ‘(wrapper native-to-managed) Test:TestFunc (int)’

通常、このようなメッセージは、マネージド関数デリゲートがネイティブファンクションに渡され、アプリケーションを構築とき必要なラッパーコードが生成されなかった場合に表示されます。どのメソッドがネイティブコードにデリゲートとして渡されるかを 指示することによって、AOT コンパイラの問題を解決できます。これは、カスタム属性 MonoPInvokeCallbackAttribute を追加して行います。現在、静的メソッドのみをネイティブコードにデリゲートとして渡すことができます。

コード例

using UnityEngine;
using System.Collections;
using System;
using System.Runtime.InteropServices;
using AOT;

public class NewBehaviourScript : MonoBehaviour {
    [DllImport ("__Internal")]
    private static extern void DoSomething (NoParamDelegate del1, StringParamDelegate del2);

    delegate void NoParamDelegate ();
    delegate void StringParamDelegate (string str);
    
    [MonoPInvokeCallback(typeof(NoParamDelegate))]
    public static void NoParamCallback() {
        Debug.Log ("Hello from NoParamCallback");
    }
    
    [MonoPInvokeCallback(typeof(StringParamDelegate))]
    public static void StringParamCallback(string str) {
        Debug.Log(string.Format("Hello from StringParamCallback {0}", str));
    }

    // 初期化にこれを使用
    void Start() {
        DoSomething(NoParamCallback, StringParamCallback);
    }
}

Xcode が以下のコンパイルエラー(メッセージ)を表示: 「ld : unable to insert branch island. No insertion point available. for architecture armv7」、「clang: error: linker command failed with exit code 1 (use -v to see invocation)」

このエラーは、通常、1 つのモジュール内にあまりにも多くのコードがあることを意味します。通常、多くのスクリプトコードか、大きな外部 .NET アセンブリがビルドに含まれたことによって発生します。さらに、スクリプトのデバッグを有効にすると、事態を悪化させる場合があります。なぜなら、各関数にかなりの数の命令が追加されるため、容易に許容限度に達してしまうからです。

Payer Sttings で managed code stripping を有効にすると問題を解決する場合があります。大きな外部 .NET アセンブリが関与している場合は特にそうです。しかし、問題が続く場合の最善の解決策は、ユーザーのスクリプトコードを複数のアセンブリに分割することです。このためのもっとも簡単な方法は、 Plugins フォルダーにいくつかのコードを移動することです。この場所にあるコードは、別のアセンブリに配置されます。また、スクリプトのコンパイルにどのように影響するかは、特殊フォルダーとスクリプトコンパイル順について情報を確認してください。

Unity iOS が現在サポートしていない機能
iOS のクラッシュバグのレポート方法