【问题标题】:Iphone App crashes when releasing view controller containing scrolling UITableView释放包含滚动 UITableView 的视图控制器时,Iphone App 崩溃
【发布时间】:2011-02-17 01:21:36
【问题描述】:

这是一个非常非常...非常奇怪的错误。很难描述我拥有的确切项目,但我会尝试创建一个更简单的类来表示我拥有的类。它是这样的:

假设我有一个导航控制器作为我的顶视图控制器。在其中,有一段时间我有一个 UIViewController,比如说一个 ContactsScreenController。此视图包含多个 UITableView,每个都由 MyTableController 类型的单独对象(委托和数据源)控制。我通过保留一组控制器来做到这一点


// This is the interface for my screen controller. An object of this type goes in a top-
// level navigation controller
// MainScreenController.h
@interface ContactsScreenController : UIViewController

    NSMutableArray* tableControllers;

@end



// MainScreenController.m

- (UITableViewCell*)cellForRowAtIndexPath..something..
{
    // Here what I do is create a new controller if needed, and add it to tableControllers
    // Memory allocations & releases are good because I checked with instruments
}

#define SAFE_DEL(x)   { if (x != nil) { [x release]; x = nil; } }

- (void)dealloc
{
    SAFE_DEL(tableControllers);
    [super dealloc];
}

现在,MyTableController 是一个更复杂的对象,因为它处理从 Web 服务获取数据,但基本上我要做的是确保在删除对象时,我会取消任何待处理的数据请求,如下所示:


// MyTableController.m
- (void)dealloc
{
    [globalDataProvider cancelRequestsForController:self];

    // release other objects i might have
    [super dealloc];
}

好的,这就是我的对象设置。当我删除对象 tableControllers 时发生崩溃。它减少了 MyTableController 对象的 retainCount 并达到 0(使用 Instruments 检查)。但是由于一些未知的原因,在保留计数为零之后,我收到了对 cancelRequestsForController 的调用。 显然,我得到了 EXC_BAD_ACCESS。

在您开始认为这是我的保留/释放对有问题之前,如果我释放主屏幕控制器而内部表是静态的,则应用程序可以完美运行。一旦滚动并点击导航控制器中的后退按钮,我就会遇到错误。

我已经使用工具检查了我的内部控制器的保留计数更改的整个历史,这很好(没有不寻常的东西)。当错误发生时,我在历史记录中的最后一个条目来自 QuartzCore run_animation_callbacks,保留计数为 -1。

有什么想法吗? :)

PS:作为启动项目的快速解决方案,我已将 cancelRequestsForController 移至单独的方法中,并在发布前为 tableControllers 中的每个对象手动调用它。这样我就确定不管tableview的状态如何,发布后都不会再有调用了。


- (void)dealloc
{
    for (TableController* c in tableControllers)
        [c cancelRequests];
    SAFE_DEL(tableControllers);
    [super dealloc];
}

但我不喜欢这个解决方案有几个原因。

【问题讨论】:

    标签: iphone uitableview crash scroll quartz-graphics


    【解决方案1】:

    感谢大家的回答/cmets。
    在我有机会释放我的对象之前,在对象中调用[super dealloc] 会产生问题。这导致了很多疯狂的事情发生。我将 [super dealloc] 移到了我的 dealloc 方法的末尾(在我发布之后),它现在可以正常工作了。

    【讨论】:

      【解决方案2】:

      SAFE_DEL 宏是不必要的,并且会降低代码的可读性。只需这样做:

      [someObject release], someObject = nil;
      

      someObject 是否已经为零也没关系,它使代码更直接可读。

      只要滚动并且我点击 导航中的后退按钮 控制器我遇到了这个错误。

      任何时候你有非内存管理逻辑,你就有脆弱性。也就是说,当dealloc 正在执行时,很可能是因为您的应用程序中有一个完整的对象子图正在被释放。由于释放顺序在很大程度上是不确定的,因此您不能安全地触发dealloc 中的任何复杂行为,而不会有向已经deallocated 对象发送消息的风险。

      最好的解决方案是将取消机制从dealloc中取出

      【讨论】:

      • 我发现 SAFE_DEL 可以使用。它使我更容易不要忘记将指针设置为 nil。而且,我知道if是没用的,只是我很久以前写的,懒得改了。使用 dealloc 更容易。这是所有对象的通用方法,我可以轻松覆盖和实现我的特殊需求。最后,dealloc不是一种析构函数吗?
      • 我同意 dealloc 中非常复杂的代码可能是个坏主意。但是,在用 [super dealloc] 解决问题后,现在我的方法在对象的 retainCount 仍为 1 时被调用。我认为在 dealloc 中使用更复杂的行为没有危险,只要它可以在更防弹的方式。
      • 是的——在 SAFE_DEL 上没什么大不了的。您可以将dealloc 视为析构函数,但问题——尽管——是对象图其余部分的完整性可能无效。这可能更容易,但意外的排序依赖和其中发生的崩溃/长时间调试会话使得它比首先将拆卸与释放分开要困难得多。
      • 另请注意,根据定义,保留计数永远不能为 0,事实上,已释放对象的保留计数在释放之前不会减为 0。
      • 为什么框架会浪费一个周期或 7 个周期将保留计数减至 0,而根据定义,该对象已死且已消失?
      【解决方案3】:

      需要检查两件事: 1.您的 globalDataProvider (或任何其他类)是否引用了您的控制器(即用数据调用它?)您的保留计数可能为零,但原因不正确。您如何分配阵列以及阵列中的每个控制器?你有@properties 吗? 2. 在您的可执行文件属性 Arguements 屏幕中,设置 NSZombieEnabled=YES 这将准确地告诉您哪个对象在其保留计数为零时被调用。 高温下, -迈克

      【讨论】:

      • 感谢您的回答。数据提供者持有对委托的弱引用。当我尝试调试它时,它开始表现得更加怪异,所以它让我思考并从大局来看问题:有人在弄乱我的记忆。最终我找到了那个小家伙……在我有机会释放我的记忆和从那时起发生的所有疯狂事情之前,它是一个 [super dealloc] 调用。我发誓一定是猫把它移到了那里,因为它不在我的存储库中,所以今晚发生了:)
      • 猫更擅长除鼠而不是调试。很高兴你修好了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-10
      • 2011-12-26
      • 1970-01-01
      • 1970-01-01
      • 2010-11-27
      • 2011-09-08
      相关资源
      最近更新 更多