重复调用 UINavigationController 的 push 导致访问错误

Posted

技术标签:

【中文标题】重复调用 UINavigationController 的 push 导致访问错误【英文标题】:Repeated call of push for UINavigationController causes access error 【发布时间】:2010-09-18 15:35:54 【问题描述】:

我有一个 TabView 应用程序,其中一个选项卡有一个 NavView 作为它的视图之一。这个视图有一个带有 TableView 的子视图来保存事件。我已启用导航栏右上角的“添加”按钮并将 IBAction 分配给该按钮。

目标是显示一个页面以添加一个新事件,该事件由我实例化子类控制器类的实例时加载的 NIB 文件定义。我还从父导航控制器传递了 managedObjectContext。

这第一次有效,但第二次按下“添加”按钮时出现 EXC_BAD_ACCESS 错误。我已经调试到将控制器推送到导航控制器上,并且我已经确认这是发生异常的地方。我已经阅读了文档,它说导航控制器将通过点击导航栏顶部提供的“后退”按钮来弹出控制器。我也知道,如果我尝试将相同的视图推送到堆栈上,将会发生未处理的异常。我什至尝试在推送之前弹回 root 以清除堆栈,但第二次推送时仍然出现异常。

我是否应该在其他地方手动弹出这个(即导航“返回”按钮实际上并没有弹出这个)?我还确认导航控制器和新实例化的视图控制器的实例都不为空。

这是添加按钮的代码 sn-p:

- (IBAction) addEvent: (id)sender 

 // Here we'd instantiate an instance of our Add Event Controller to show the form that allows us to enter a new event.

 // We'd add the context to the class from here so that it can get to our Core Data


  EventEntryViewController *fvController = [[EventEntryViewController alloc] initWithNibName:@"AddEventView" bundle:nil];

 fvController.managedObjectContext = self.managedObjectContext;

 [self.navigationController pushViewController:fvController animated:YES];

 [fvController release];

 fvController = nil;


感谢您的帮助。

更新:

好的,我已经阻止了异常的发生。我尝试了一个非常简单的视图,其中没有任何内容,没有 IBOutlets 或 IBActions 以及一个没有属性的控制器类。那行得通,所以我认为这一定是我的 EventEntryViewController 的问题。它并没有做太多事情,并且回溯显示它在显示视图的内部正在死去,所以我什至没有进入 loadView 方法,更不用说我的代码的任何其他部分了。我有一些在上一次加载中已经初始化的类属性,作为一个好公民,我在 viewDidUnload() 中释放了它们,并在 dealloc() 中释放了它们。当我注释掉我的类属性的 dealloc 时,它起作用了!

我对此感到困惑,并将 NSLog() 行放在 viewDidLoad()、viewDidUnload() 和 dalloc() 中。这是我取出类属性的 dalloc 后两个推送序列的结果:

2010-09-18 20:17:46.224 myFuel[6435:207] EventTableNavViewController: viewDidAppear
2010-09-18 20:17:51.391 myFuel[6435:207] **EventEntryViewController**: View Did Load // loading up my class
2010-09-18 20:17:53.954 myFuel[6435:207] EventTableNavViewController: viewWillAppear
2010-09-18 20:17:54.314 myFuel[6435:207] EventTableNavViewController: viewDidAppear
2010-09-18 20:17:54.315 myFuel[6435:207] **EventEntryViewController**: dalloc       // after the class should have been popped
2010-09-18 20:18:02.803 myFuel[6435:207] **EventEntryViewController**: View Did Load //loading up my class
2010-09-18 20:18:08.134 myFuel[6435:207] EventTableNavViewController: viewWillAppear
2010-09-18 20:18:08.494 myFuel[6435:207] EventTableNavViewController: viewDidAppear
2010-09-18 20:18:08.495 myFuel[6435:207] **EventEntryViewController**: dalloc    // after the class should have been popped

我在这里遗漏了什么吗?视图不应该调用 viewDidUnload() 从而释放类属性吗?我可以看到 dealloc 被调用,但我在哪里释放这些?

【问题讨论】:

【参考方案1】:

根据你所说的有几件事,虽然我不清楚你在做什么。

首先,viewDidUnload: 不能保证被调用。一般来说,只有当您的视图控制器卸载它的视图以响应内存警告时,它才会被调用。因此,您在 viewDidLoad: 中保留的任何引用或任何连接的 IBOutlets 都应该在 viewDidUnload:dealloc 中发布。

其次,您说您“在 dealloc 中释放它们”是指您的“类属性”,我认为这意味着您实际上是在其他对象上调用 dealloc。如果是这样……不要那样做。您只能在已保留的对象上调用release,并且当您直接调用dealloc 时很少会有实例。这是因为dealloc 将在最后一次发布时在对象的release 实现中被调用。这就是保留/释放系统的全部意义所在。

【讨论】:

谢谢。我一直在阅读有关 Objective-C 尤其是 ios 框架中的内存管理的内容。我不得不深入研究旧 MS COM 世界中关于引用计数的突触,以便灯泡熄灭。为了清楚起见,在正常操作中会调用 dealloc,并且我的释放(如果它是对象的最后一个引用)将被 GC 拾取。在出现内存警告时,将调用 viewDidUnload 并且那里的版本将允许 GC 清理内容。问题是 viewDidUnload AND dealloc 在最后一种情况下会被调用,随后的发布会导致异常吗? 当您在viewDidUnload: 内发布时,请务必同时设置对nil 的引用。然后,当您在 dealloc 中调用 release 时,您不会在悬空引用上调用 release。因此,例如,如果您有一个 IBOutlet UILabel* label,来自 'viewDidUnload:`,您将执行:[label release]; label = nil;dealloc 中的相同代码。如您所知,您可以安全地向nil 引用发送消息,这就是dealloc 中的发布是安全的原因。

以上是关于重复调用 UINavigationController 的 push 导致访问错误的主要内容,如果未能解决你的问题,请参考以下文章

可以为单个 presentModalViewController:animated 多次调用 viewDidAppear:animated 方法吗?

UINavigationController

UINavigationController 风格

全屏显示 ViewController

多控制器

UINavigationControllerDelegate 不起作用