弹出到根视图控制器,没有表视图的动画崩溃
Posted
技术标签:
【中文标题】弹出到根视图控制器,没有表视图的动画崩溃【英文标题】:Pop to root view controller without animation crash for the table view 【发布时间】:2011-03-31 12:34:26 【问题描述】:我在标签栏控制器中有 3 个视图控制器。单击任何选项卡会在导航堆栈中加载其根视图控制器。
例如选项卡 1、选项卡 2 和选项卡 3。 导航堆栈 (tab2VC2) 中的第二个视图控制器有一个 tableView。 点击tab2 show VC in tab2,然后点击tab1,尝试进入它的rootVC。然后应用程序崩溃说
[用户详细信息VC 表视图:cellForRowAtIndexPath:]: 发送到已释放实例的消息 0xe0a23b0
如果我用动画 popToRootVC 那就没问题了。我发现在 tab2VC2 中调用了 viewDidAppear,其中调用了 tableView.reloadData,然后是 dealloac,似乎与此同时 reloadData 开始工作,表被释放。在动画的情况下,它需要一些时间,所以它不会崩溃。但是没有动画,它就会崩溃。 你认为,它是一个 iPhone 错误?还是我做错了?既然弹出到根控制器有一个没有动画的选项,它应该可以工作,不是吗?
#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];
【问题讨论】:
你能发布崩溃吗?只是这段代码没有用。 AFAIK 这个问题一直存在于 UIKit 中,直到 ios 8(包括在内),从 iOS 9 开始,这个问题得到了解决(至少在我的应用程序中)。 【参考方案1】:我认为这是 UIKit 的一个错误或至少是一个弱点,但我已经为此浪费了大半天的时间,所以我现在不打算用示例代码编写它并报告给 Apple。如果其他人想这样做,我将不胜感激。
这就是我认为幕后发生的事情。你有一个 UITableViewController,我们称它为 myTable,在 UINavigationController 的堆栈上,并且导航堆栈是隐藏的,因为它位于未选择的选项卡或其他任何东西上。然后,您调用 [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];
欢迎任何额外的智慧。
【讨论】:
我相信这已在 iOS 6 上得到修复。当然,我们看到了与此完全匹配的崩溃,并且 Crashlytics 没有显示来自 iOS 6 设备的任何报告,我们只在 iOS 上看到它5. 我遇到了类似的问题(仍然在 iOS 6 中)。崩溃发生在 tableView:titleForFooterInSection: 中。但是,似乎仍然是来自触发的 reloadData。消除数据源似乎没有帮助(因此可能不是同一个问题)。但是,我发现一个 hack 解决方法是保留 topViewController,然后在 popToRoot 调用之前使用 performSelector 进行延迟释放。哈基,但它奏效了。很想知道为什么会发生这种情况以及更好的解决方法。 我只是想说声谢谢!在我的应用程序中被困了一周,这修复了它。谢谢你!!如果我们将 tableview 作为属性并在 dealloc 中有 release,我们是否将 release 保持在 nil 语句的上方或下方? 令人讨厌的是,我们收到了一个来自 iOS 6 设备的崩溃报告,这是由该代码引起的。我认为崩溃只是因为我们启用了 ARC。从回溯来看,访问 self.tableView 的一个副作用是导致视图被加载,然后当我们的 viewDidLoad 在部分被破坏的视图控制器上调用时会导致问题......我将尝试用 2 行包围if (self.isViewLoaded)
.
@JosephH:您是否尝试过以下 Pyraego 的代码,该代码针对 ARC 支持进行了修改?如果可以解决,请告诉我,我会将其整合到答案中。由于我不使用 ARC,所以我没有自己测试过。【参考方案2】:
杰西的回答很完美。我刚刚对 ARC Support 做了一点修改
- (void)dealloc
self.tableView.delegate = nil;
self.tableView.dataSource = nil;
【讨论】:
【参考方案3】:在 Groups&Files 列表中双击您的可执行文件;单击参数选项卡;单击下方窗格中的“+”,输入“NSZombieEnabled”,将值设置为YES,选中该框,单击红点关闭。 现在再次运行您的测试用例,它会告诉您哪个对象已被释放。 我怀疑这是在 tab2V2 中支持您的 tableView 的数组。 确保您的内存处理正确(是否正确保留和释放?)。
【讨论】:
我说的是tableView, [UserDetailVC tableView:cellForRowAtIndexPath:]: message sent to deallocated instance 0xe0a23b0 您使用什么数据结构来提供数据?换句话说,在 cellForRowAtIndexPath 中,您从哪里获取数据以填充单元格?你可以发布你实例化它的代码吗?我认为它在你认为它之前被释放了。【参考方案4】:你可以试试这个来避免这个问题:
-(void)viewWillAppear:(BOOL)animated
if (animated)
[self.tableView reloadData];
当从标签栏控制器导航时,视图不会被动画化。因此,如果视图在弹出到根之前仅出现一瞬间,它就不会尝试重新加载表数据。
【讨论】:
【参考方案5】:有一个类似的问题,老实说,我很确定它与新的 SDK 有关。以下代码之前运行良好。我有一个导航控制器和一个新的导航控制器,上面有 3 个不同的视图(想想:更改密码,你有 3 个不同的步骤:控制、更改、重复。)如果用户输入错误的密码,你将首先被“更改密码控制器”推送,然后被推送到第一页(注销)。
这在以前有效:
-(void)logout
[self.presentedViewController dismissModalViewControllerAnimated:TRUE];
self.localNavigationController = nil;
[self.navigationController popToRootViewControllerAnimated:TRUE];
解决此问题的方法是将 ViewWillAppear 更改为 ViewDIDAppear。你可能有一些滞后,但至少它不会崩溃:)
【讨论】:
【参考方案6】:虽然 Jesse 的回答提供了对围绕这个问题的语义的洞察,但在我的情况下,根本原因是不同的。只是想我会提到它,以防有人发现自己处于同样的情况。
我看到了错误消息
* -[UIAnimator removeAnimationsForTarget:]:消息发送到释放的实例...
有问题的对象是 UITableViewCell。经过检查,我的代码中没有内存分配问题。
问题原来是我从非 UI 线程调用 popViewController:animated:
系列方法之一。创建此线程是为了运行一些网络操作和数据库处理任务。我只是将pop方法的调用移到UI线程,发现我停止收到错误。
【讨论】:
以上是关于弹出到根视图控制器,没有表视图的动画崩溃的主要内容,如果未能解决你的问题,请参考以下文章