cappuccinoのコンピタブログ

cappmac.exblog.jp
ブログトップ

[Xcode] Exception Breakpointで例外発生箇所を特定

iOSアプリ開発で良く躓くポイントとして、実行時エラーがありますよね。

なんか分からないけどアプリが落ちる、なんかレスポンスが悪い、とかいろいろ有ると思います。
前者に対してはXcodeのLLDBが、後者に対してはInstrumentsが役立つと思います。

今回は、Xcodeのデバッグで役立った機能があったので、メモとして残そうと思います。

と、その前に、前々回の投稿で、「やりたいことを実現するコード」と「のり付けするための知識」という考えに着目して記事にしていこうみたいな宣言をした割に、前回はその部分が不明瞭だった気がします。

そのため、今後、開発メモとして残す記事は、必ず上記が見えるようにしたいと思います。

  • 今回のやりたいこと「現在地周辺のお店情報を収集したい」

このために、以下のコードを参考に(つまりコピペ)しました。

https://github.com/koogawa/VenueListGPS

このソースはここここで詳細に解説されています。

今回、この記事中の「エラー処理は省略されています」をちゃんと読んでいなかったばかりにドツボにはまりました。

実行すると、必ずアプリが落ちる、というエラーが発生してしまいました。
ただし、発生するタイミングは一定しないため、ネットワーク処理部分に問題が有るのでは、と推測しました。

エラーの内容は「'NSInvalidArgumentException', reason: 'data parameter is nil'」というものです。停止するとUIApplicationMainで止まり、スタックトレースは見れる訳ですが、発生箇所を直感的に突き止めるのはかなり難航しそうな感触です。

まずは、怪しそうな箇所を抜粋してみました(VenueListTableViewController.m)。

- (void)locationManager:(CLLocationManager *)manager

didUpdateToLocation:(CLLocation *)newLocation

fromLocation:(CLLocation *)oldLocation

{

// 緯度・経度取得

CLLocationDegrees latitude = newLocation.coordinate.latitude;

CLLocationDegrees longitude = newLocation.coordinate.longitude;

// APIからベニューリストを取得

NSString *urlString = [NSString stringWithFormat:@"https://api.foursquare.com/v2/venues/search?ll=%f,%f&limit=30&client_id=ICIWPLPZATTTPYV0YBSVB4AQCF2PVXUWKHS3ZT1BURV0PS02&client_secret=T5SEMJSHYURT5UGERXLZNCUGI1QZ1JJHWBYN2XLDWK3FQUFN&v=20140629", latitude, longitude];

//NSLog(@"urlString = %@", urlString);

NSURL *url = [NSURL URLWithString:urlString];

NSString *response = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];

NSData *jsonData = [response dataUsingEncoding:NSUTF32BigEndianStringEncoding];

NSDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];

// エラーコードをログに出力

NSInteger errorCode = [[[jsonDic objectForKey:@"meta"] objectForKey:@"code"] integerValue];

NSLog(@"errorCode = %d", errorCode);

// 結果取得

NSArray *venues = [[jsonDic objectForKey:@"response"] objectForKey:@"venues"];

venues_ = [venues mutableCopy];

[self.tableView reloadData];

}


JSONが絡む処理で問題が起きる模様であるとGoogle先生の結果から推測できたので、

NSDictionaryのエラー処理をちゃんとしてみました。


NSError *error;


NSDictionary *jsonDic = [NSJSONSerialization

JSONObjectWithData:jsonData

options:kNilOptions

error:&error];


さらに、NSArrayの取り出しは、nilをチェックし、nilじゃないときだけ処理を実行するよう修正しました。
つまりこんなかんじ。

if (!error) {

// エラーコードをログに出力

if ([jsonDic count] == 0) {

NSLog(@"don't access it as the index is out of bounds");

return;

}else{

NSInteger errorCode = [[[jsonDic objectForKey:@"meta"] objectForKey:@"code"] integerValue];

NSLog(@"errorCode = %ld", (long)errorCode);

// 結果取得

NSArray *venues = [[jsonDic objectForKey:@"response"] objectForKey:@"venues"];

venues_ = [venues mutableCopy];

}

   }else{

NSLog(@"Error: %@", [error localizedDescription]);

}



結果は変わらず・・・

ふと、UIApplicationMainで止まってしまう点についてGoogleで調べていたときに効果的にデバッグするための情報を知りました。(もう return UIApplicationMain で止まっても困らない! Xcodeでのデバッグ方法:Zero4Racer PRO Developer's Blog
これを使うと例外発生時の実行コードでブレークポイントを自動で打って停止してくれるというものです。

  • ブレークポイントペインでAdd Exception Breakpointを選択する。
c0064553_11345038.png
  • 対象例外をすべて「All」とする。
    c0064553_11345505.png


試しにやってみたところ、出ました。ブレークポイントがわかりました!
NSDataの処理をした直後のコードで停止していることが分かりました。
基本的なことですが、NSData型のjsonDataを受け取った直後のエラーチェックが抜けていた訳です。

そこで、JSONのパース処理をする部分はNSDataのチェックでnilでない場合に実行するようにしました。
修正後はこのような感じになりました。

if (jsonData == nil) {

NSLog(@"ERROR!");

}else{

NSDictionary *jsonDic = [NSJSONSerialization

JSONObjectWithData:jsonData

options:kNilOptions

error:&error];

if (!error) {

// エラーコードをログに出力

if ([jsonDic count] == 0) {

NSLog(@"don't access it as the index is out of bounds");

return;

}else{

NSInteger errorCode = [[[jsonDic objectForKey:@"meta"] objectForKey:@"code"] integerValue];

NSLog(@"errorCode = %ld", (long)errorCode);

// 結果取得

NSArray *venues = [[jsonDic objectForKey:@"response"] objectForKey:@"venues"];

venues_ = [venues mutableCopy];

}

}else{

NSLog(@"Error: %@", [error localizedDescription]);

}


}



早速、NSDataのnilチェック処理を追加して、実行してみたところ、5時間経っても動き続けています。
解決しました。
実行した結果は以下のような感じです。
c0064553_11480243.png

  • 今回ののり付けのための知識「サンプルコードはエラーチェックがないものと心して使う」「実行時エラーはツールで特定する」

[PR]
by cappuccino_mac | 2014-07-01 06:43 | 開発メモ