このチュートリアルはiOSチュートリアルメンバーの Matthijs Hollemans 氏によって執筆されました。氏はiOSデベロッパー、デザイナーとして活躍しています。興味のある方はGoogle+ and Twitterへどうぞ。
ある薄暗い雨の夕刻、楽しくアプリ開発に尽力していた貴方に突然の出来事が、、、ボンッ、 「あぁぁぁーーーーークラッシュしたぁーーーーー!!!」(悲しいバイオリンの音)
ここで貴方の取る行動は…: まず、落ち着いてください!
クラッシュを修正する事は決して難しい事ではありません。ここで焦ってランダムに修正を試みる事は事態を悪化させるだけです。必要な事はクラッシュを通してその原因を学び、正しいアプローチで修正して行く事です。
まず何よりも、どのファイルのどのコードがクラッシュを引き起こしたのかを探る必要があります: そんな時、Xcodeデバッガが強い味方となります。しかしまず始めに、Xcodeデバッガの利用方法をきちんと学ぶ必要があるのです。このチュートリアルではそこの所を解説して行きますね。
このチュートリアルは全ての開発者、初心者から上級者の為にご用意させていただきました。たとえ貴方が経験豊かな開発者であったとしても、今まで貴方の知らなかったヒントが隠されているかもしれません。
始めましょう!
始めに今回のチュートリアルで使用するサンプルプロジェクトをダウンロードしてください。Xcodeでプロジェクトを開くと、少なくとも8個の警告が出ているのが分かりますね。あっ、それと今回のチュートリアルではXcode 4.3を使用しています。しかしバージョン 4.2でも大丈夫です。
Note: このチュートリアルの解説ではアプリをiOS 5のシミュレーターで使用する必要があります。もしこのアプリを実機で動かしても、やっぱりクラッシュします。しかしもしかしたら同じ順序でクラッシュが起こるとは限りません。
「おいおい、クラッシュしちまったぜ!」:-]
基本的にはクラッシュには2種類あります:SIGBART(EXC_CRASH)とEXC_BAD_ACCESS(SIGBUSもしくはSIGSEGV)です。
クラッシュが起こる上でSIGBARTは良い奴です。と言うのも、コントロールされたクラッシュだからです。つまりプログラムの中でやっちゃ行けない事をやった場合、システムがコントロールしてアプリを停止させるんです。
一方、EXC_BAD_ACCESS。こいつはデバッグするのが少し厄介な奴です。何故なら、これはアプリがエラーを起こした時のみ発生する為です。大抵の場合はメモリ管理が原因で引き起こされます。
さてプロジェクトを見てみると、最初のクラッシュはSIGBARTですね。これはツイてますね。SIGBARTはXcodeのデバッグアウトプットウインドウ(右下の表示領域)にメッセージが表示されます。今回の場合は以下のように表示されるはずです:
Problems[14465:f803] -[UINavigationController setList:]: unrecognized selector sent to instance 0x6a33840 Problems[14465:f803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationController setList:]: unrecognized selector sent to instance 0x6a33840' *** First throw call stack: (0x13ba052 0x154bd0a 0x13bbced 0x1320f00 0x1320ce2 0x29ef 0xf9d6 0x108a6 0x1f743 0x201f8 0x13aa9 0x12a4fa9 0x138e1c5 0x12f3022 0x12f190a 0x12f0db4 0x12f0ccb 0x102a7 0x11a9b 0x2792 0x2705) terminate called throwing an exception |
ここに表示されたエラーメッセージを解読する方法を学ぶ事は非常に重要です。と言うのも、何所が悪いのか間違っているのかの重要な手がかりが含まれているからです。ここで興味深いパートは:
[UINavigationController setList:]: unrecognized selector sent to instance 0x6a33840 |
エラーメッセージ、“unrecognized selector sent to instance XXX”とはアプリが存在しないメソッドをコールしようとしている事を示唆しています。もしくはメソッドが違ったオブジェクトをコールしている場合にも起こります。ここでは問題のオブジェクトはUINavigationController(メモリでのアドレスは0x6a33840)で、メソッドはsetList:です。
クラッシュの原因を知る事は重要です。しかし、まずは何処でエラーが引き起こされているかを突き止める事から始めましょう。それにはソースファイルの名前と問題を引き起こしている行ナンバーを見つける必要があります。これはコールスタック(スタックトレースもしくはバックトレースとも呼ばれます)を使用する事で実行することが出来ます。
アプリがクラッシュした時、Xcodeの左端ウインドウがデバッグナビゲーターに切り替わります。ここではアプリでアクティブなスレッドが表示され、クラッシュしたスレッドがハイライトされます。通常はアプリのメインスレッドであるThread 1にハイライトされます。もし、コードがキューやバックグラウンドスレッドを使用している場合は、他のスレッドが同様にハイライトされます。
ここでは、main.mのmain()関数が問題の原因らしいですね。どの道ここでは情報はあまり得られませんでした。もっと深く掘り下げてみる必要がありますね。
コールスタックをもっと見るには、デバッグナビゲーターの下のスライダーを右端までドラッグしてください。そうするとクラッシュの瞬間の全てのコールスタックを見る事が出来ます:
リストに表示されているアイテムは関数、メソッドもしくはiOSフレームワークです。デバッガはアプリをポーズさせ、これらの関数とメソッドはその瞬間のままでフリーズして表示されます。
一番下のstart()関数が最初にコールされています。そしてこれを実行する関数がmain()となります。ここがアプリのスターティングポイントとなります。これらは常にリストの最下層周辺に表示されます。main()の次はUIApplicationMain()が呼ばれます。これはエディタウィンドウで緑の矢印でポイントされる行です。
スタックを更に見ると、UIApplicationMain()がUIApplicationオブジェクトの_runメソッドをコールして、更にCFRunLoopRunInMode()をコール。そしてCFRunLoopRunSpecific()がコールされ、最終的に__pthread_killまでコールされます。
コールスタックでのこれらの関数とメソッドは、main()を除いて灰色で表示されます。それはこれらがビルトインのiOSフレームワークからきている為です。これらにソースコードはありません。
ソースコードを持つ物はmain.mだけです。例えクラッシュの原因でなくともXcodeはエディタに表示させます。この事が度々、開発初心者を混乱させます。しかしこれからの解説で「あぁ、なーるほど、そういうことね」と納得して頂けると思いますよ。
それでは、とりあえずスタックトレースのアイテムのどれでも1つをクリックしてみてください。はい、何やら理解不能のコードがたくさん表示されましたね:
エクセプションexceptionブレークポイント
それでは、一体全体どうやってアプリをクラッシュさせた問題のコードの行を探し出せば良いんでしょうか?えーっと、スタックトレースがこの様になった時は、アプリによってエクセプションexception(例外)が引き起こされているんです。(この事はコールスタックにobjc_exception_rethrowが表示される事で確認できます)
エクセプションはプログラムが何かしらの例外(エクセプション)、つまりやっちゃ行けない事に引っかかった時に発生します。今目の前にある物はその例外の直後です。つまりXcodeはその結果を表示しているわけです。理想を言えば、そのエクセプションが投げかけられた箇所が分かれば良いわけです。
幸運にもXcodeにはその為の機能があるんです。XcodeにException Breakpointを使って、その瞬間にプログラムをポーズさせることが出来ます。ブレークポイント(Breakpoint)は特定の瞬間にプログラムをポーズさせる事が出来るデバッガツールです。この機能についてはチュートリアルのパート2でもっと深く解説します。でも今は、エクセプションが投げかけられる直前でプログラムをポーズさせる為に特定のブレークポイントを使いましょう。
Exception Breakpointを設定するには、Breakpoint Navigatorに切り替えましょう:
下の小さな+ボタン、これをクリックしてAdd Exception Breakpointを選択します::
すると新しいブレークポイントがリストに追加されます:
Doneボタンをクリックしてポップアップを消します。これでXcodeのツールバーのブレークポイントが作動します。アプリをブレークポイント無しで作動させたい時はこのボタンをOFFに切り替えるだけです。でも今はONのままでアプリをRunさせましょう。
やった!これでエディタにソースコードの特定の行が指し示されました。さっきとは大違いです。左のコールスタックも表示が変わっている事に注目してください。それでは見てみましょう。
ふーむ、どうやら原因の行はAppDelegateのapplication:didFinishLaunchingWithOptions:メソッドのようです:
viewController.list = [NSArray arrayWithObjects:@"One", @"Two"]; |
それではもう1度エラーメッセージを見てみましょう:
[UINavigationController setList:]: unrecognized selector sent to instance 0x6d4ed20 |
コードでは“viewController.list = なんちゃら”がsetListをコールしています。この“list”はMainViewController クラスのプロパティです。しかし、エラーメッセージによると、viewController変数はMainViewControllerのオブジェクトではなくUINavigationController をポイントしています。もちろん、UINavigationController には“list”プロパティは存在しません。なるほど、ここが何やら怪しいですね!
それではStoryboardファイルを開いて、rootViewControllerのプロパティが何を指し示しているのか見てみましょう:
そういうことか!StoryboardのView ControllerがNavigation Controllerに設定されています。なるほど、これで何故window.rootViewControllerがMainViewControllerではなく、UINavigationControllerのオブジェクトを指し示しているのかが理解出来ました。これを修繕するには、application:didFinishLaunchingWithOptions:を以下に書き換えます:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { UINavigationController *navController = (UINavigationController *)self.window.rootViewController; MainViewController *viewController = (MainViewController *)navController.topViewController; viewController.list = [NSArray arrayWithObjects:@"One", @"Two"]; return YES; } |
始めにself.window.rootViewControllerからUINavigationControllerを参照してから、navigation controllerのtopViewControllerを訪ねる事でMainViewControllerへのポインタを取得します。そうする事で、viewController変数が適切なオブジェクトをポイントする事が出来るようになります。
Note:“unrecognized selector sent to instance XXX”のエラーが出た時は、オブジェクトが正しいタイプであるか、そしてそのメソッドで使われているタイプの名前が正しいかどうかをチェックしましょう。多くの場合、間違ったオブジェクトのメソッドをコールしている場合があります。
初めてのメモリエラー
これまでで最初のエラーの関する問題は修正出来ました。アプリをRunしてください。おーっと、今度は又同じ行でクラッシュしてしまいました。しかし今回はEXC_BAD_ACCESSのエラーですね。この場合、問題はメモリ管理にあります。
メモリ管理に関する問題が引き起こすクラッシュは、ピンポイントで特定する事が困難な時があります。と言うのも今指定されている行よりも早い段階で引き起こされている場合があるからです。もし不具合の原因となるコードがメモリストラクチャを破壊している場合、結果として実際にエラーが引き起こされた時点よりも後になって表示されるからです。この場合、全く関係のない行を指し示している事になります。
実際の所、バグはテスト中には出てこないかもしれません。そしてユーザーの実機でその姿を現すのです。これは最悪ですね。絶対に阻止しなければなりません。
このクラッシュの修正も実はとても簡単です。ソースコードエディタを見てみると、Xcodeは始めからこの行に警告を出しています。行数の左にある黄色の三角のマークが分かりますか?これはコンパイラの警告を示しています。この三角をクリックすると、以下の様にXcodeは“Fix-it”のポップアップを表示します:
コードはオブジェクトのリストでNSArrayを初期化しています。そして、そのリストは最後にnilを使用しなければなりません。しかし、ここには“, nil”が見当たりません。そして監視員はオブジェクトが存在していないと判断したため、警告を発していました。そしてNSArrayが存在しないオブジェクトを読み込もうとした為、混乱をきたし、その結果としてアプリがクラッシュしたんですね。
この間違いは、“Fix-it”オプションを選択するか、nilを加える事で修正出来ます:
viewController.list = [NSArray arrayWithObjects:@"One", @"Two", nil]; |
“This class is not key value coding-compliant”
それでは再度アプリをRunして、この他にどのようなバグがこのプロジェクトに仕込まれているか見てみましょう。そして、どん!クラッシュです。main.mですね。Exception Breakpointは機能しています。そして今回はソースコードの何所にもハイライトはありません。はい、今回のクラッシュはソースコードで引き起こされた物ではありません。つまりコールスタックはアプリ内のメソッドが原因ではなく、main()に問題があると言ってるんですね。
リストのメソッド名を上から下まで見てみると、NSObjectとKey-Value Codingがありますね。その下に[UIRuntimeOutletConnection connect]なるメソッドがコールされています。これが一体何なのか、私には分かりません。しかし、アウトレットの接続に関するメソッドのようです。その下にnibファイルからViewをロードしているメソッドがありますね。これで大方の検討はつきましたね:
しかしこれまでとは違って、XcodeのDebug Paneには都合のいいエラーメッセージはありません。これはまだ実際にはエラーが引き起こされていない為です。つまり、Exception Breakpointはエラーの原因を表示する直前でプログラムをポーズさせている為です。Exception Breakpointは時として、部分的なエラーメッセージを表示する時もあれば、表示しない時もあるんです。
エラーメッセージをちゃんと確認するには、デバッガのツールバーの“Continue Program Execution”をクリックします:
エラーメッセージを表示させる為には1回以上クリックする必要があるかもしれません:
Problems[14961:f803] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key button.' *** First throw call stack: (0x13ba052 0x154bd0a 0x13b9f11 0x9b1032 0x922f7b 0x922eeb 0x93dd60 0x23091a 0x13bbe1a 0x1325821 0x22f46e 0xd6e2c 0xd73a9 0xd75cb 0xd6c1c 0xfd56d 0xe7d47 0xfe441 0xfe45d 0xfe4f9 0x3ed65 0x3edac 0xfbe6 0x108a6 0x1f743 0x201f8 0x13aa9 0x12a4fa9 0x138e1c5 0x12f3022 0x12f190a 0x12f0db4 0x12f0ccb 0x102a7 0x11a9b 0x2872 0x27e5) terminate called throwing an exception |
これまでの様に、下の数字達は無視しましょう。これらはコールスタックを表しています。そして、、、出ました。読み取れる物が表示されていますね。ここにヒントが隠されているのかもしれません。
えーっと、なになに?:
- NSUnknownKeyException
- MainViewController
- “this class is not key value coding-compliant for the key button”
NSUnknownKeyExceptionはエクセプションの名前で、エラー箇所を検索する際の良いインジケーターとなります。NSUnknownKeyExceptionによると、どこかに“unknown key”(正体不明のkey)が存在しているようです。おそらくそれは、MainViewControllerファイルでkeyの名前は“button”のようですね。
これまでで、全てはnibからロードしている時にエラーが引き起こされている事が分かっています。アプリはStoryboardというよりむしろnibファイルを使っていて、Storyboardはnibファイルの集合体です。ですからStoryboardが何かしらのエラーを引き起こしている張本人だと言えます。
MainViewControllerのアウトレットをチェックしてみましょう:
Connections InspectorからViewの真ん中に配置されたUIButtonが分かりますね。このボタンはMainViewControllerの“button”アウトレットに接続されています。以上の事からStoryboard/nibは“button”というアウトレット名を参照しているわけですが、エラーメッセージによると、どういう訳かこのアウトレットが見つからないと言うのです。
まずはMainViewController.hを見てみましょう:
@interface MainViewController : UIViewController @property (nonatomic, retain) NSArray *list; @property (nonatomic, retain) IBOutlet UIButton *button; - (IBAction)buttonTapped:(id)sender; @end |
@propertyで定義された、アウトレット“button”がありますね。一体全体このアウトレットの何が悪いのでしょうか?コンパイラの警告を注意深く見てみると、もうお分かりですね。
もし「えっ、何がおかしいの?」と仰るなら、MainViewController.mの@synthesizeリストを見てみてください。お分かりになりましたか?
“button”のプロパティが@synthesizeされていません。これではエラーが出るのも仕方がないですね。
以下をMainViewController.mの @synthesizeの行の下に加える事でこの問題は解決出来ます:
@synthesize button = _button; |
これでクラッシュはしないはずです。それではRunしてみましょう:
Note:エラー“this class is not key value coding-compliant for the key XXX”は、通常はnibからロードする際に参照するプロパティが存在しない時に起こります。つまりコードからアウトレットのプロパティを削除したにもかかわらず、nibファイルから接続を解除していない場合に起こるようです。
ボタンを押してみよう
これでアプリは正常に起動はしました。しかしこれで安心してはいけません。ボタンをタップしてみましょう。
「えぇぇぇぇっ!」またですか?はい、ごめんなさい。今度のクラッシュはmain.mでSIGBARTですよ!:
Problems[6579:f803] -[MainViewController buttonTapped]: unrecognized selector sent to instance 0x6e44850 |
ここでもスタックトレースは役に立ちません。たくさんのメソッドが相互にイベントとアクションを送り合っているのは分かります。でも大丈夫。注目すべきアクションはもう分かっていますね。つまりはUIButtonをタップするとその結果としてIBAction がコールされています。
もちろん、このエラーメッセージには見覚えがありますね。そうです。存在していないメソッドがコールされているんです。今回のターゲットオブジェクトはMainViewControllerです。えーっと、どうも間違ってはいないようなんですけど、、、。MainViewController.hを見てみるとIBActionは以下の様になっていますね:
- (IBAction)buttonTapped:(id)sender; |
あれっ?何か違和感が、、、。エラーメッセージはメソッド名はbuttonTappedだと言ってますね。しかしMainViewControllerのメソッド名はbuttonTapped:となっています。メソッド名の最後に引数(“sender”)をとる場合必要なコロンがついています。一方エラーメッセージのメソッド名にはコロンがありません。つまり引数がない事になります。あれぇーー?
- (IBAction)buttonTapped; |
何が起こったのでしょうか?考えられる事は、メソッドは元々は引数を持っていなくて、後に引数に“sender”が付け加えられたと見るのが妥当でしょう。しかし、Storyboardでアップデートされなかったのです。
この事はStoryboardのConnections Inspectorで確認出来ます:
まず始めに、Touch Up Insideのイベントの接続を解除(小さなXをクリック)します。そして再びMain View Controllerの接続します。この時、今回は buttonTapped:メソッドを選択します。Connections Inspectorのメソッド名にコロンが追加されている事に注目してください。
それではアプリをRunしてボタンをタップしてください。「ふぉぉぉぉっ!?」「えっ、また?」はい、またです。今度は“unrecognized selector”のメッセージがありますね。「ちゃんとコロンを付けて“buttonTapped:”にしたでしょぉぉぉっ!!」
Problems[6675:f803] -[MainViewController buttonTapped:]: unrecognized selector sent to instance 0x6b6c7f0 |
落ち着いてよく見てください。今回も又、コンパイラワーニングが解決方法を表示しているでしょう?Xcodeが MainViewControllerの実装が未完成だと文句言ってるんです。ほら、はっきりとbuttonTapped:のメソッド定義が見つからないと言ってますね。
それではMainViewController.mを見てみましょう。ちゃんとbuttonTapped: と表示されてますね。「だったら、なんでっっっ!?」「??」「あれっ?」「Oh…」「スペルが間違ってるじゃないですかぁぁぁ!!!」
- (void)butonTapped:(id)sender |
はい、直すのは簡単ですね。メソッド名を以下に置き換えましょう:
- (void)buttonTapped:(id)sender |
メソッドをIBActionとして宣言していない事に注目してください。これでもオーケーなんです。こっちの方がカッコいいと思うならこちらでどうぞ。
Note:このような間違いはコンパイラワーニングを注意深く見ていれば簡単に見つける事が出来ます。個人的には全てのワーニングを重大なエラーとして捉えその一つ一つをアプリをRunsさせる前に直す事を心がけています。この様にXcodeはこういった小さな間違いをちゃんと教えてくれます。注意深く開発を進める事が大切です。
メモリを弄くる
さて、もういつもの流れはお分かりですね。アプリをRunして、ボタンをタップして、そしてクラッシュを待ちます。ぼんっ!!はい、クラッシュです:
あーーっと、今度はまた別のEXC_BAD_ACCESSが出ました。でもラッキーですね。今回はクラッシュを引き起こした箇所がピンポイントで指し示されていますね。buttonTapped:です:
NSLog("You tapped on: %s", sender); |
今回のようなクラッシュの原因は、少し考えてしますかもしれませんが、Xcodeにもう1度聞いてみましょう。黄色い三角をタップして、、、ふむ:
NSLog()はObjective-Cのstringを使います。C言語のstringではありません。つまり、@をインサートする事で直せます:
NSLog(@"You tapped on: %s", sender); |
しかし、警告の黄色い三角が消えませんね。これはこの行に他のバグが存在して知る事を表しています。今回のバグはちょっと興味深い代物です。ある時はちゃんと動いて、、少なくとも問題が無い様に見えて、ある時はクラッシュする。もちろん、これは開発している貴方の実機では何も起きず、ユーザーの実機でクラッシュするという最悪のパターンです。
さて、警告を見てみましょう:
ここで使われている%sはCのスタイルです。Cの文字列はその最後にNUL(0)を使用します。例として、C言語の文字列で“Crash!”はメモリ上で以下の様に表します:
C言語の文字列を使用する関数やメソッドは、その文字列の最後に値0を付ける事が決まりとなっています。そうしなければ関数は文字列が終わったと認識出来ません。
つまり、NSLog()もしくはNSStringのstringWithFormatで%sを使うには文字列をフォーマットする必要があります。そうすることで引数があたかもCの文字列かの様に翻訳してくれます。この場合の引数は“sender”で、Cの文字列ではないUIButtonのオブジェクトへ参照しています。もし“sender”が0を含むオブジェクトを参照している場合はNSLogはクラッシュしません。しかし、アウトプットでは以下の様に表示されてしまいます:
You tapped on: xËj |
これがどういう事なのかちょっと見てみましょう。アプリをRunしてボタンをタップしてクラッシュさせましょう。ここでDebug Paneの左から“sender”を右クリックして、オプションから“View Memory of *sender”を選択します。
するとXcodeはメモリアドレスのコンテンツを表示します。これこそがNSLog()がプリントアウトした物です。
しかしここにNULが表示されているとは限りませんが、EXC_BAD_ACCESSエラーは簡単に引き起こされるのです。アプリの起動テストに常にシミュレーターを使用する場合には、いつもエラーが起こるとは限りません。故に今回のようなバグは非常にトレースする事が困難だと言えるでしょう。
もちろん、このような間違ったフォーマットで記述されている場合はXcodeは常に警告を発します。しかしC言語の文字列を使用する場合や、メモリを直接操作する場合には、他の人(ユーザー)のメモリに影響を与えない様に常に細心の注意を払うべきです:
もしアプリが常にクラッシュしてバグが発見しやすい場合は非常に幸運で、ほとんどの場合には時々クラッシュする程度です。そしてその事実こそがクラッシュそのものの再生を非常に困難にしていると言えるでしょう。
それではNSLog()を以下に書き換えてください:
NSLog(@"You tapped on: %@", sender); |
それではアプリをRunしてボタンを再度タップしてみてください。これでNSLog()は正常に表示される様になりましたね。しかし、それでもまだbuttonTapped:に問題があるようです。
デバッガと友達になりましょう
今回のクラッシュに関して、Xcodeは以下の行を指しています:
[self performSegueWithIdentifier:@"ModalSegue" sender:sender]; |
Debug Paneにはメッセージはありません。それでは先ほど様にContinue Program Executionをクリックしてみましょう。しかし今回はエラーメッセージを表示させる為にデバッガにコマンドをタイプして実行してみましょう。この方法の利点はアプリが同じ箇所でポーズしたままだと言う事です。
シミュレーターからRunする場合には、以下を(11db)プロンプトの後にタイプしてください:
(lldb) po $eax |
LLDBはXcode 4.3以降のデフォルトのデバッガです。もし古いバージョンのXcodeを使用している場合はGDBデバッガになります。この2つは基本のコマンドをシェアしていて、Xcodeが(11db)の代わりに(gdb)を表示している場合にも以下のコマンドは問題なく使用出来ます(XcodeのScheme editorでデバッガを切り替える事が出来ます)。
poコマンドは“print object”を表します。$eaxはCPUレジスタの1つを表します。このエクセプションの場合には、このレジスタはNSExceptionオブジェクトを参照します。Note:$eaxはシミュレーター上でしか使用出来ません。もし、実機でデバッグする場合は$r0を使用する必要があります。
例えば、以下の様にタイプする場合は:
(lldb) po [$eax class] |
以下の様に表示されます:
(id) $2 = 0x01446e84 NSException |
数字は重要ではありませんが、NSExceptionオブジェクトに関係がある事は間違いないようです。
この方法を使えばNSExceptionからあらゆるメソッドをコールする事が出来ます。例えば:
(lldb) po [$eax name] |
上記はエクセプション名を取得する事が出来ます。今回はNSInvalidArgumentException、そして:
(lldb) po [$eax reason] |
上記はエラーメッセージを取得します:
(unsigned int) $4 = 114784400 Receiver () has no segue with identifier 'ModalSegue' |
Note:“po $eax”だけなら、“description”メソッドをコールして表示させます(エラーメッセージを含む)。
これで何所が間違っているのかが表示されました。つまり「Hey,Hey! ユーはセグエで“ModalSegue”を使おうとしているんだろ?でもそんなモンMainViewControllerにはないんだメーン!」と言っているんですね。
Storyboardにはセグエは表示されています。しかしこれは良くあるミスなんですが、セグエのidentifierを設定していないんです。
セグエのidentifierを“ModalSegue”に変更して下さい。それではアプリをランして下さい。立ち上がるのを待ってボタンをタップしてください。これでもうクラッシュはしなくなりましたね。でも、あれっ?ちょっと待って下さいよ。Table Viewって何にも表示されなくていいんでしたっけ?あれっっっ?
次は何を勉強しましょうか?
「いやいやいや、ですからなんでTable Viewが空っぽなんですか?」まぁまぁ、それは次のパート(Part Two)で解決しましょう。Part Twoではデバッグのツールとして、NSLogやブレークポイント、そしてゾンビ等の新兵器を投入します。
これらの事が全て完了したら、アプリは正常に作動します。しかしそれよりも大切な事は、実際にアプリを作成する段階でこのような状況に陥った時の為にデバッグをスキルを向上させておく事が何よりも大切です。何故なら貴方がデベロッパーである以上、必ずこの壁にブチ当たるからです。
このチュートリアルに関してのご意見等はフォーラムよりそうぞ!
このチュートリアルはiOSチュートリアルメンバーの Matthijs Hollemans 氏によって執筆されました。氏はiOSデベロッパー、デザイナーとして活躍しています。興味のある方はGoogle+ and Twitterへどうぞ。
アプリがクラッシュしちゃった。さて、どうしましょう- Part 1 is a post from: Ray Wenderlich
The post アプリがクラッシュしちゃった。さて、どうしましょう- Part 1 appeared first on Ray Wenderlich.