views:

1327

answers:

1

While trying out an experimental UINavigationController-based iPhone application, I ran into a problem when the user navigates back to the previous view.

The simple application uses a UINavigationController, onto which new instances of UIViewControllers are pushed. These instances are all of the same class (in this example, class MyViewController a sub-class of UIViewController), and are manually created (not using a NIB). Each instance contains a separate UITableView instance as the UIViewController's view.

The following tableView:didSelectRowAtIndexPath: method is from the MyViewController class. It creates and pushes another MyViewController instance onto the navigationController when the user selects a table cell:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyViewController *nextViewController = [[MyViewController alloc] initWithNibName:nil bundle:nil];
    [self.navigationController pushViewController:nextViewController animated:YES];
    [nextViewController release];
}

The user can navigate forwards through a sequence of views, each one containing a table. The problem occurs when navigating back to the previous screen. The application aborts and xcode starts the debugger.

The error can be prevented by not releasing the MyViewController instance in the tableView:didSelectRowAtIndexPath: method above, or by not calling dealloc on the 'myTableView' instance in MyViewController's dealloc method. However, that's not a real solution. As far as I know, the UINavigationController "owns" the pushed UIViewController instance, which can then safely be released from the client that allocated it. So, what can be wrong with this experimental app? Why would it terminate when the user navigates back?

Below are a few other methods of the MyViewController class:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        self.title = @"My Table";
        myTableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        myTableView.delegate = self; 
        myTableView.dataSource = self;
        self.view = myTableView;
    }
    return self;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyTable"];
    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithFrame:CGRectMake(0,0, 300, 50) reuseIdentifier:@"MyTable"];
        [cell autorelease];
    }
    cell.text = [NSString stringWithFormat:@"Sample: %d", indexPath.row];
    return cell;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 3; // always show three sample cells in table
}

- (void)dealloc {
    [myTableView dealloc];
    [super dealloc];
}

EDIT:

Problem fixed - thanks Rob Napier for pointing out the problem.

The -loadView method now sets up the view using a local UITableView instance:

- (void)loadView {
    UITableView *myTableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    myTableView.delegate = self; 
    myTableView.dataSource = self;
    self.view = myTableView;
    [myTableView release];
}
+2  A: 

You're setting the view in the wrong method. You should set this up in -loadView, not -initwithNibName:bundle:. Look in View Controller Programming Guide "Using View Controllers" for an example.

Rob Napier