【问题标题】:Dealloc Being Called Twice?Dealloc 被调用两次?
【发布时间】:2011-06-26 15:59:22
【问题描述】:

重温!

感谢 Lone Gunman,这个问题是由于在释放之前没有将许多代表设置为 nil 的疏忽。

这是一个奇怪的...我熟悉基本的内存管理,但我认为我所看到的有些不寻常。这里有一点背景...

我有一个 NavigationController 来处理以下 ViewController 之间的导航:

首页 -> 游戏 -> 游戏

运行代码时,它会在离开游戏时掉落。在 GameViewController 中有一个 dealloc 方法,类似于:

- (void)dealloc 
{
    [board release];
    [opponentsViewController release];
    [instructionsViewController release];
    [imgPicker release];
    [gameView release];
    [super dealloc];
}

当导航控制器返回到游戏列表(从游戏中)时,它会抛出一个 EXC_BAD_ACCESS。所以我调出我可信赖的分析器并检查僵尸。唉,正如我预期的那样,一条消息正在发送到一个已释放的对象!深入挖掘,我发现该对象的历史记录中有 3 个条目:

  1. Board 被分配(由 Game 的 init 方法调用)
  2. Board 被释放(由 Game 的 dealloc 方法调用)
  3. Board 被僵尸化(由 Game 的 dealloc 方法调用)

调用 2 和 3 都是从 UINavigationController setDisappearingViewController 调用的。

在我的 dealloc 方法中,我为每个释放调用设置断点,这样做时 - 发生 [board release] 调用,然后发生 [opponentsViewController release] 调用,然后再次发生 [board release] 调用。所以我看到 dealloc 方法没有完全完成并再次调用。

这可能是什么原因?

编辑:这是GameViewController Implementation

Games 控制器中添加此游戏的代码:

-(void) gotoGame:(int)tag {

    game = [[GameViewController alloc] init];
    [self.navigationController pushViewController:game animated:YES];
    [game release];

}

编辑:这是GameViewController Header

【问题讨论】:

  • -[NSObject dealloc] 不能执行两次。如果您在构建设置中启用“运行静态分析器”并尝试再次构建,您会遇到什么问题?
  • 当您多次调用 release 时会发生这种情况。你能检查一下你没有在程序的其他任何地方发布 Board 而不保留它吗?>
  • @WTP:静态分析器没有投诉。
  • 你释放和保留董事会多少次?
  • 编辑:添加 GameViewController 的实现文件

标签: objective-c ios ios4 memory-management


【解决方案1】:

我会尝试将所有 ivar 的代表设置为零(编辑:在 dealloc 中)。我在获取结果控制器时遇到了类似的问题。无法在 dealloc 中将其委托设置为 nil,并且在释放视图控制器时核心数据堆栈仍然有一个指向它的指针。

所以这是我的赌注,在 dealloc 中将 ivar 委托设置为 nil,尽管我看不到您的标头以知道您确定符合哪些协议。

编辑:解释

设置委托实际上是给正在执行委托的对象一个指针(我相信它通常是一个分配的属性)。

@property (assign) delegate;

我会以我遇到的问题为例。

假设你有一个视图控制器,它有一个 fetchedResultsController 作为 ivar。当您设置 FRC 委托时:

fetchedResultsController.delegate = self;

并且视图控制器被释放,任何使用该指针的对象仍然认为它是活动的。你可能会想,既然 FRC 也在 dealloc 中发布,你会没事的(这就是为什么我花了 4 天时间才弄清楚这一点:)),但有时实现的其他部分也会使用你的委托。所以解决方法是:

-(void)dealloc
{
    self.fetchedResultsController.delegate = nil;
    [_fetchedResultsController release];
    [super dealloc];
}

注意:一旦新工具可供所有人使用,您就不必再担心这些东西了^ ^;

【讨论】:

  • 这是一个非常好的观点。这是标题,这里有一堆代表……我想这是正确的方向。
  • 已修复,谢谢!我希望有人可以帮助填补我的知识空白。代表们做了什么让 dealloc 看起来像是被解雇了两次?他们的方法中的 [super dealloc] 调用是否会导致它再次触发?
  • 我在答案中添加了解释。
  • 你是个天才。救了我。有趣的是 iOS 5 没有这个问题,但是当我在 iOS 4.3 上测试时,它开始在 dealloc 上崩溃。
【解决方案2】:

试试

- (void) dealloc {
    if(game != nil){
     //release here
     [game release];
    }
    [super dealloc];
  }

顺便说一句,您似乎已经在头文件中声明了游戏,并且在推送之后您正在释放它,并且在 dealloc 方法中您正在释放它。要么从 dealloc 方法中删除 release 调用,要么像这样改变你的方法。

-(void) gotoGame:(int)tag {

    GameViewController *game = [[GameViewController alloc] init];
    [self.navigationController pushViewController:game animated:YES];
    [game release];

}

更新

此外,您没有在任何地方使用该标签。你为什么不像这样创建你的 init 方法

GameViewController *game = [[GameViewController alloc] initWithTag:tag];
[self.navigationController pushViewController:game animated:YES];
[game release];

【讨论】:

  • 在发送 release(或任何其他方法)之前检查是否为 nil 是没有用的。在 nil 上调用方法是一个 noop。它什么也不做。因此,您的建议也不会解决任何问题。
  • @Johan Kool 至少他的应用不会崩溃。
  • 你没有抓住重点。向nil 发送-release 消息将不会使他的应用程序崩溃。此外,对象在被释放后不会自动变为nil(或者,换句话说,nil 不表示垃圾值)。所以支票是没用的。 (-dealloc 实现中通常做的一件事是将指针归零,这可以防止崩溃,但也可以隐藏错误。
  • Rahul,你绝对不知道(或忽略)基本的内存管理规则,通过建议 if+release 的东西,而且你不知道(或忽略)消息可以是的事实发送到 nil ...
猜你喜欢
  • 2011-11-17
  • 1970-01-01
  • 1970-01-01
  • 2015-04-06
  • 1970-01-01
  • 2011-12-17
  • 1970-01-01
  • 2016-04-01
  • 2012-05-05
相关资源
最近更新 更多