IOS 5シミュレーター/デバイスでXcode 4.2のデバッグに問題があります。予想どおり、次のコードがクラッシュします。
NSArray *arr=[NSArray array];
[arr objectAtIndex:100];
IOS 4では、16進数の有用なスタックトレースを取得します。しかし、iOS 5では、次のことができます。
*** First throw call stack:
(0x16b4052 0x1845d0a 0x16a0674 0x294c 0x6f89d6 0x6f98a6 0x708743 0x7091f8 0x7fcaa9 0x2257fa9 0x16881c5 0x15ed022 0x15eb90a 0x15eadb4 0x15eaccb 0x6f02a7 0x6faa93 0x2889 0x2805)
ありがとう。
これを修正しようとするものは何もありませんでした(両方のコンパイラー、両方のデバッガーなどを試してみました)。
ただし、効果的な回避策を見つけました-独自の例外ハンドラーを作成します(他の理由でも役立ちます)。最初に、エラーを処理し、コンソールに出力する関数を作成します(また、それを使ってやりたいことは何でも)。
void uncaughtExceptionHandler(NSException *exception) {
NSLog(@"CRASH: %@", exception);
NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
// Internal error reporting
}
次に、アプリデリゲートに例外ハンドラーを追加します。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
// Normal launch stuff
}
それでおしまい!
これが機能しない場合は、考えられる2つの理由のみがあります。
NSSetUncaughtExceptionHandler
呼び出しを上書きしています(アプリ全体に対してハンドラーは1つだけです)。たとえば、一部のサードパーティライブラリは独自のuncaughtExceptionHandlerを設定します。したがって、didFinishLaunchingWithOptions
関数のENDで設定してみてください(または、サードパーティのライブラリを選択的に無効にします)。さらに良いことに、NSSetUncaughtExceptionHandler
にシンボリックブレークポイントを設定して、誰が呼び出しているかをすばやく確認します。あなたがしたいことは、別のものを追加するのではなく、現在のものを修正することです。EXC_BAD_ACCESS
はnot例外です。以下の@Erik Bのコメントの功績)例外ブレークポイントを追加する便利なオプションがあります(ブレークポイントナビゲータの下部にある+を使用)。これは、すべての例外で中断します(または条件を設定できます)。この選択が4.2で新しくなったのか、それともシンボルが見つからないという問題を回避しようとしているのにようやく気付いたのかどうかはわかりません。
このブレークポイントに到達すると、Debug Navigatorを使用して、通常どおりコールスタックをナビゲートしたり、変数を調べたりできます。
コピー/貼り付けなどに適したシンボリックな呼び出しスタックが必要な場合は、そこからgdbバックトレースが正常に機能します。
(gdb) bt
#0 0x01f84cf0 in objc_exception_throw ()
#1 0x019efced in -[NSObject doesNotRecognizeSelector:] ()
(等)
デバッガには新しい機能があります。 4.0で発生していたように、例外がスローされるたびにブレークポイントを設定し、そこで実行を停止できます。
[Breakpoint Navigator]で[Exception Breakpoint]を追加し、オプションのポップアップで[Done]を押します。
それで全部です!
PS:場合によっては、Objective-Cの例外に対してのみブレークした方が良い場合があります。
ここにもう1つの解決策がありますが、以前ほどエレガントではありませんが、例外ブレークポイントまたはハンドラーを追加しなかった場合、その方法はたった1つです。
アプリがクラッシュし、生のファーストスローコールスタック(16進数)を取得したら、Xcodeコンソールに入力しますinfo line *hex
(スターと0x
16進指定子)、例:
(gdb) info line *0x2658
Line 15 of "path/to/file/main.m" starts at address 0x25f2 <main+50>
and ends at 0x267e <main+190>.
lldbを使用している場合は、image lookup -a hex
(この状況では星なし)、および同様の出力が得られます。
このメソッドを使用すると、スロースタックの最上部(約5〜7個のシステム例外プロパゲーターがあります)からクラッシュを引き起こした関数まで移動し、正確なファイルとコード行を決定できます。
また、同様の効果を得るには、ターミナルでatosユーティリティを使用して、次のように入力します。
atos -o path/to/AplicationBundle.app/Executable 0xAdress1 0xAdress2 0xAdress3 ...
シンボリックスタックトレースを取得します(少なくともデバッグシンボルがある関数の場合)。このメソッドは、各アドレス呼び出しにinfo line
、コンソール出力からアドレスをコピーし、ターミナルに貼り付けます。
例外ブレークポイント(ブレークポイントナビゲータの下部にある+を使用)とアクションを追加bt
を追加できます([アクションを追加]ボタンをクリックします) 、デバッガーコマンドを選択し、テキストフィールドに「bt」と入力します)。これにより、例外がスローされるとすぐにスタックトレースが表示されます。
Xcodeのデバッグコンソールプロンプトタイプ:
image lookup -a 0x1234
そして、次のようなものが表示されます。
Address: MyApp[0x00018eb0] (MyApp.__TEXT.__text + 91088)
Summary: MyApp`-[MyViewController viewDidAppear:] + 192 at MyViewController.m:202
これは一般的な問題であり、4.2ではスタックトレースを取得しません。 LLDBとGDBを交換して、より良い結果が得られるかどうかを確認できます。
ここにバグレポートを提出してください。
http://developer.Apple.com/bugreporter/
編集:
LLVM GCC 4.2にスワップバックすると、これが起こることはないと思います。ただし、必要な機能が失われる可能性があります。
メイン関数で次のコードを使用します。
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal;
@try {
retVal = UIApplicationMain(argc, argv, nil, nil);
}
@catch (NSException *exception) {
NSLog(@"CRASH: %@", exception);
NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
}
@finally {
[pool release];
}
return retVal;
}
「Compile for Thumb」をオンに戻す(デバッグ構成)ことはうまくいきました。