Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pop to root view controller without animation crash for the table view

I have 3 view controller in a tab bar controller. Clicking on any tab loads its root view controller in the navigation stack.

e.g. tab1, tab2, and tab3.
The 2nd view controller in the navigation stack (tab2VC2), has a tableView. Click on tab2 show VC in tab2, then tap on tab1, tries to go to its rootVC. Then the app is crashing saying

[UserDetailVC tableView:cellForRowAtIndexPath:]: message sent to deallocated instance 0xe0a23b0

If I popToRootVC with animation then its okay. I found viewDidAppear in the tab2VC2 is called where the tableView.reloadData is called, then dealloac, seems in the meantime reloadData starts working, the table is released. in case of animation, it gets some time, so it dont crash. But without animation, it is crashing. Do you think, its an iPhone bug? or I am doing wrong? Since pop to root controller have an option without animation, it should work, not it?

#pragma mark Tab bar controller delegate
- (void)tabBarController:(UITabBarController *)tbController didSelectViewController:(UIViewController *)viewController {
    int i = tbController.selectedIndex;
    NSArray *mycontrollers = tbController.viewControllers;
    [[mycontrollers objectAtIndex:i] popToRootViewControllerAnimated:NO];
}
like image 367
karim Avatar asked Mar 31 '11 12:03

karim


2 Answers

I consider this a bug or at least a weakness in UIKit, but I've already blown half my day on it, so I'm not going to write it up with example code and report it to Apple right now. If someone else wants to do that, I would appreciate it.

Here's what I think is going on under the hood. You have a UITableViewController, let's call it myTable, on the stack of a UINavigationController, and that navigation stack is hidden because it's on an unselected tab or whatever. Then, you call [myTable.tableView reloadData], and iOS cleverly optimizes by not reloading the data right away, because the user won't be seeing it anyway if it's on a hidden tab. Instead, the reload request is deferred and stored somewhere for when the view is shown. But before it can be shown, you pop myTable off the navigation stack. When myTable's original tab is shown, the reload request gets executed, but its dataSource is no longer there, so it's a bad access.

Now from my tests with a subclass of UITableViewController that uses the automatically provided tableView property (not loaded from a NIB file), the UITableView is not being deallocated when myTable deallocates as in the situation above. That would be fine, except the default dealloc implementation for UITableViewController does not clear the dataSource property of the UITableView (which was set by the default implementation of init).

So, there are probably a couple good workarounds, like deferring the request to reloadData yourself, but the simplest one I can think of is putting this in the implementation of your UITableViewController subclass:

- (void)dealloc {
  ...
  self.tableView.delegate = nil;
  self.tableView.dataSource = nil;
  [super dealloc];
}

Any additional wisdom would be most welcome.

like image 69
Jesse Crossen Avatar answered Oct 19 '22 04:10

Jesse Crossen


Jesse's answer works perfect. I just made a slight modification for ARC Support

- (void)dealloc 
{
  self.tableView.delegate = nil;
  self.tableView.dataSource = nil;
}
like image 33
mikemike396 Avatar answered Oct 19 '22 02:10

mikemike396