我在标签栏控制器中有3个视图控制器。单击任何选项卡会在导航堆栈中加载其根视图控制器。
e.g。 tab1,tab2和tab3。
导航堆栈中的第二个视图控制器(tab2VC2)有一个tableView。
单击tab2,在tab2中显示VC,然后点击tab1,尝试转到rootVC。然后应用程序崩溃说
[UserDetailVC 的tableView:的cellForRowAtIndexPath:]: 消息发送到解除分配的实例 0xe0a23b0
如果我用动画popToRootVC那么没关系。我发现tab2VC2中的viewDidAppear被调用,其中tableView.reloadData被调用,然后是dealloac,似乎在此期间reloadData开始工作,表被释放。在动画的情况下,它会有一些时间,所以它不会崩溃。但没有动画,就会崩溃。 你觉得,这是一个iPhone的bug吗?或者我做错了?由于pop到root控制器有一个没有动画的选项,它应该可以工作,不是吗?
#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];
}
答案 0 :(得分:36)
我认为这是一个错误或者至少是UIKit的一个弱点,但是我已经把它的一半时间都吹了,所以我不打算用示例代码编写它并立即报告给Apple。如果其他人想这样做,我将不胜感激。
这就是我认为在幕后发生的事情。你有一个UITableViewController,让我们在UINavigationController的堆栈上调用它myTable,并且导航堆栈是隐藏的,因为它位于未选中的选项卡或其他任何位置。然后,你调用[myTable.tableView reloadData],并且通过而不是立即重新加载数据来巧妙地优化iOS,因为如果它位于隐藏的选项卡上,用户将无法看到它。相反,重新加载请求被延迟并存储在显示视图的某处。但在显示之前,您可以将myTable从导航堆栈中弹出。显示myTable的原始选项卡时,会执行重新加载请求,但其dataSource不再存在,因此访问权限不佳。
现在我的UITableViewController子类使用自动提供的tableView属性(不是从NIB文件加载)进行测试,当myTable如上面的情况解除分配时,UITableView没有被释放。这没关系,除了UITableViewController的默认dealloc实现没有清除UITableView的dataSource属性(由init的默认实现设置)。
所以,可能有一些很好的解决方法,比如自己将请求推迟到reloadData,但我能想到的最简单的方法是将它放在UITableViewController子类的实现中:
- (void)dealloc {
...
self.tableView.delegate = nil;
self.tableView.dataSource = nil;
[super dealloc];
}
任何额外的智慧都会受到欢迎。
答案 1 :(得分:4)
杰西的回答非常完美。我刚刚对ARC Support进行了一些修改
- (void)dealloc
{
self.tableView.delegate = nil;
self.tableView.dataSource = nil;
}
答案 2 :(得分:0)
在“组和文件”列表中双击您的可执行文件;单击“参数”选项卡;单击下方窗格中的“+”,输入“NSZombieEnabled”,将值设置为YES,选中该框,单击红点以关闭。 现在再次运行您的测试用例,它将告诉您哪个对象已被解除分配。 我怀疑它是支持tab2V2中tableView的数组。 确保您的内存处理正确(是否保留并正确释放?)。
答案 3 :(得分:0)
您可以尝试以避免此问题:
-(void)viewWillAppear:(BOOL)animated
{
if (animated)
{
[self.tableView reloadData];
}
}
从标签栏控制器导航时,视图不会设置动画。因此,如果视图在弹出到根之前只是暂时出现,它将不会尝试重新加载表数据。
答案 4 :(得分:0)
有一个类似的问题,我很确定它与新SDK有关,说实话。以下代码以前工作正常。我有一个导航控制器和一个新的导航控制器,有3个不同的视图可以放到它上面(想想:更改密码,你有3个不同的步骤:控制,更改,重复。)如果用户键入错误的密码,你将首先被推送“更改密码控制器”,然后被推到第一页(注销)。
之前有效:
-(void)logout {
[self.presentedViewController dismissModalViewControllerAnimated:TRUE];
self.localNavigationController = nil;
[self.navigationController popToRootViewControllerAnimated:TRUE];
}
您可以通过将ViewWillAppear更改为ViewDIDAppear来解决此问题。你可能有一些lagg但至少它不会崩溃:)
答案 5 :(得分:0)
虽然Jesse的回答提供了对这个问题的语义的深入了解,但就我而言,根本原因是不同的。我以为我会提到它,以防有人发现自己处于相同的情况。
我看到了错误消息
* - [UIAnimator removeAnimationsForTarget:]:发送到解除分配实例的消息...
有问题的对象是UITableViewCell。检查时,我的代码中没有内存分配问题。
问题是我从非UI线程调用popViewController:animated:
系列方法之一。已创建此线程以运行某些网络操作和数据库处理任务。我只是将pop方法的调用移动到UI线程,发现我没有收到错误。