もう return UIApplicationMain で止まっても困らない! Xcodeでのデバッグ方法

Xcode4.2 エラー画面
Xcode4.2 エラー画面

Xcode4になってから、いまいちデバッグがうまくいかない理由に、止まってしまう場所が、

return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

の行で止まってしまう場合が多いのがありますよね。この時に、どこで止まったのか分かるときはいいですが、いろいろな画面の中でどこで止まったか分からないときはデバッグ困りますよね。その対策法を見つけたので書いておきます。

試しに、エラーが起こるプロジェクトを作ってみました。

- (void)viewDidLoad
{
NSMutableArray *arrray = [NSMutableArray arrayWithCapacity:0];
[arrray objectAtIndex:10];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

Arrayのサイズを0にして、10にアクセスするとエラーが起きます。この時のエラー画面が、
Xcode4.2 エラー画面
Xcode4.2 エラー画面

この画面なのですが、コンソールには、

2012-02-24 15:33:19.530 test[30669:f803] *** Terminating app due to uncaught exception ‘NSRangeException’, reason: ‘*** -[__NSArrayM objectAtIndex:]: index 10 beyond bounds for empty array’
*** First throw call stack:
(0x13b9052 0x154ad0a 0x13a5db8 0x23d7 0xd664e 0x36a73 0x36ce2 0x36ea8 0x3dd9a 0xebe6 0xf8a6 0x1e743 0x1f1f8 0x12aa9 0x12a3fa9 0x138d1c5 0x12f2022 0x12f090a 0x12efdb4 0x12efccb 0xf2a7 0x10a9b 0x211d 0x2085 0x1)
terminate called throwing an exception(lldb)

と出ていて、objectAtIndex: が問題だとは分かりますが、どこかは分からず、しかも止まった場所が、UIApplicationMainです。この時に、問題の位置を特定出来たら一番ですよね。

対策1:止まったところでブレークポイントが発生するようにしてみる。

一つ目の方法は、例外が発生したら即停止するブレークポイントを作成する方法です。

Exception ブレークポイントの作成
Exception ブレークポイントの作成

このように、ブレークポイントペインの左下の+から、例外ブレークポイントを作成します。
そのままDone
そのままDone

こんな感じになります。
例外ブレークポイントが作成
例外ブレークポイントが作成

これでアプリを再実行すると
エラーが起きたところで止まる
エラーが起きたところで止まる

このように、エラーが起きたところで止まりました!

対策2:詳細なエラーコンソールを取得

一番目の方法でもエラーの起きた状況で止まらない場合がありました。その時は、詳細なエラースタックを表示するようにプログラムを修正出来ます。AppDelegate.mを次のように変更します。この方法を使用する場合は、対策1の、ブレークポイントを削除してから行ってください

//
// AppDelegate.m
// test
//
// Created by Tomohisa Takaoka on 2/24/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "AppDelegate.h"

void uncaughtExceptionHandler(NSException *exception) {
NSLog(@"CRASH: %@", exception);
NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
// Internal error reporting
}

@implementation AppDelegate

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
// Override point for customization after application launch.
return YES;
}

NSSetUncaughtExceptionHandlerという関数を作成して、それを、application didFinishLaunchingWithOptions:関数で呼び出すようにしておくだけです。
そうしてもう一度実行すると、このようになります。

詳細なスタックが表示される
詳細なスタックが表示される

この方法だと、UIApplicationMainで止まるのですが、詳細なスタックが表示され、ここから、どこで止まったのか推測することが出来ます。この場合に表示されたスタックは以下の通りです。

2012-02-24 16:01:37.187 test[30994:f803] CRASH: *** -[__NSArrayM objectAtIndex:]: index 10 beyond bounds for empty array
2012-02-24 16:01:37.219 test[30994:f803] Stack Trace: (
0 CoreFoundation 0x013b906e __exceptionPreprocess + 206
1 libobjc.A.dylib 0x0154ad0a objc_exception_throw + 44
2 CoreFoundation 0x013a5db8 -[__NSArrayM objectAtIndex:] + 264
3 test 0x00002377 -[ViewController viewDidLoad] + 119
4 UIKit 0x000d664e -[UIViewController view] + 184
5 UIKit 0x00036a73 -[UIWindow addRootViewControllerViewIfPossible] + 45
6 UIKit 0x00036ce2 -[UIWindow _setHidden:forced:] + 280
7 UIKit 0x00036ea8 -[UIWindow _orderFrontWithoutMakingKey] + 49
8 UIKit 0x0003dd9a -[UIWindow makeKeyAndVisible] + 35
9 UIKit 0x0000ebe6 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1820
10 UIKit 0x0000f8a6 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 508
11 UIKit 0x0001e743 -[UIApplication handleEvent:withNewEvent:] + 1027
12 UIKit 0x0001f1f8 -[UIApplication sendEvent:] + 68
13 UIKit 0x00012aa9 _UIApplicationHandleEvent + 8196
14 GraphicsServices 0x012a3fa9 PurpleEventCallback + 1274
15 CoreFoundation 0x0138d1c5 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53
16 CoreFoundation 0x012f2022 __CFRunLoopDoSource1 + 146
17 CoreFoundation 0x012f090a __CFRunLoopRun + 2218
18 CoreFoundation 0x012efdb4 CFRunLoopRunSpecific + 212
19 CoreFoundation 0x012efccb CFRunLoopRunInMode + 123
20 UIKit 0x0000f2a7 -[UIApplication _run] + 576
21 UIKit 0x00010a9b UIApplicationMain + 1175
22 test 0x0000200d main + 141
23 test 0x00001f75 start + 53
24 ??? 0x00000001 0x0 + 1
)
2012-02-24 16:01:37.313 test[30994:f803] *** Terminating app due to uncaught exception ‘NSRangeException’, reason: ‘*** -[__NSArrayM objectAtIndex:]: index 10 beyond bounds for empty array’
*** First throw call stack:
(0x13b9052 0x154ad0a 0x13a5db8 0x2377 0xd664e 0x36a73 0x36ce2 0x36ea8 0x3dd9a 0xebe6 0xf8a6 0x1e743 0x1f1f8 0x12aa9 0x12a3fa9 0x138d1c5 0x12f2022 0x12f090a 0x12efdb4 0x12efccb 0xf2a7 0x10a9b 0x200d 0x1f75 0x1)
terminate called throwing an exception(lldb)

とくに、 2 CoreFoundation 0x013a5db8 -[__NSArrayM objectAtIndex:] + 264
3 test 0x00002377 -[ViewController viewDidLoad] + 119
の部分で、どの関数で問題が起きたか分かりますね。これで関数をたどる事も可能になります。

これによって、開発が非常に行いやすくなりますね。

こちらのStackoverflowの記事を参考にさせていただきました。ios – Xcode 4.2 debug doesn’t symbolicate stack call – Stack Overflow

こちらの記事もよろしければどうぞ
iOS 開発で、EXC_BAD_ACCESS とさよならするための6つのルール « Zero4Racer PRO Developer’s Blog
Xcode 4.2 で、デバッグ中に急遽 NSDictionary のデータを確認する方法 « Zero4Racer PRO Developer’s Blog
iOS 5 公開記念! Objective-Cのメモリ管理の革命、 ARC 超入門(サンプルはgitHubに公開) « Zero4Racer PRO Developer’s Blog
カクカクしないアプリを作りたいiOSプログラマにオススメ!エキスパートObjective-Cプログラミング « Zero4Racer PRO Developer’s Blog

「もう return UIApplicationMain で止まっても困らない! Xcodeでのデバッグ方法」への4件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください