現象:
テーブルビューで、行を選択後にpopToRootViewControllerAnimatetd:で親のビューコントローラに戻そうとしたらEXC_BAD_ACCESSになった。引数がNOの場合のみ発生。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = indexPath.section;
NSUInteger row = indexPath.row;
[tableView reloadData];
[self.navigationController popToRootViewControllerAnimated:NO];
}
原因:
親のビューコントローラに戻す前に、reloadDataでセルの再描画を行なっていた(選択されたものにチェックマークをつける目的だった)。reloadDataにより呼ばれたtableView:cellForRowAtIndexPath:内で参照していたメンバ変数が、popTo…によって既に破棄されていたで落ちているっぽい。
修正:
popToRootViewControllerAnimatedをdelay付きで実行
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = indexPath.section;
NSUInteger row = indexPath.row;
[tableView reloadData];
[self performSelector:@selector(popToRoot:) withObject:nil afterDelay:0.1];
}
- (void)popToRoot:(id)sender {
[self.navigationController popToRootViewControllerAnimated:NO];
}
追記
発生条件をもう少し調べた。以下のコードで、UITableViewのボタンを押して前に戻るのを何度か操作するとEXC_BAD_ACCESSが発生する。Xcode4.5.2 iPhone 6.0 Simulator。non ARC
わかっている条件:
- UITableViewControllerでは発生せず、UIViewControllerにUITableViewを貼りつけた場合にのみ発生
- cell.accessoryViewに値を入れなければ発生しない
EBSelectViewController.h
# import <UIKit/UIKit.h>
@interface EBSelectViewController : UIViewController<UITableViewDataSource, UITableViewDelegate> {
NSUInteger selectedIndex;
}
@end
EBSelectViewController.m
# import "EBSelectViewController.h"
@implementation EBSelectViewController
- (void)viewDidLoad {
UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
tableView.dataSource = self;
tableView.delegate = self;
[self.view addSubview:tableView];
[tableView release];
selectedIndex = 0;
}
# pragma mark -
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
cell.textLabel.text = NSLocalizedString(@"may error", nil);
cell.accessoryView = nil;
if (indexPath.row == selectedIndex) {
cell.accessoryView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"check.png"]] autorelease];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
selectedIndex = indexPath.row;
[tableView reloadData];
[self.navigationController popToRootViewControllerAnimated:NO]; //NG
// [self.navigationController popToRootViewControllerAnimated:YES]; //OK
// [self performSelector:@selector(delayedPopToRoot:) withObject:nil afterDelay:0.1]; //OK
}
# pragma mark -
- (void)delayedPopToRoot:(id)sender {
[self.navigationController popToRootViewControllerAnimated:NO];
}
@end
親となるViewController
@implementation FLViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton *naviToTestButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
naviToTestButton.frame = CGRectMake(100.0f, 50.0f, 100.0f, 44.0f);
[naviToTestButton setTitle:@"test" forState:UIControlStateNormal];
[naviToTestButton addTarget:self action:@selector(testButtonTaped:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:naviToTestButton];
}
- (void)testButtonTaped:(id)sender
{
EBSelectViewController *controller = [[EBSelectViewController alloc] init];
controller.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
@end