【问题标题】:Double releasing when it shouldn't be happening当它不应该发生时双重释放
【发布时间】:2009-06-29 22:34:00
【问题描述】:

我真的对此感到困惑。我相信我正在以正确的方式管理内存,但执行代码表明我正在双重释放该对象。这是代码,然后我会解释发生了什么。

@protocol SomeDelegate <NSObject>
@required
- (id)initWithCols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
@end


@interface SomeObject : NSObject <SomeDelegate> {
}
- (id)initWithCols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
@end


@interface Layout : UIView {
  id<SomeDelegate> someDelegate;
}
@property(retain) id<SomeDelegate> someDelegate;
- (id)initWithFrame:(CGRect)aRect Cols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
@end


@implementation Layout
@synthesize someDelegate;
- (id)initWithFrame:(CGRect)aRect Cols:(NSUInteger)Cols Rows:(NSUInteger)Rows {

  if(self = [super initWithFrame:aRect]) {
    cols = Cols; 
    rows = Rows;
    id<SomeDelegate> delegate = [[SomeObject alloc] initWithCols:cols Rows:rows];
    [self setSomeDelegate:delegate];
    //[delegate release];
  }
  return self;
}

-(void)dealloc {
    [someDelegate release];
    [super dealloc];
}

@end

现在,当我取消注释“//[代表发布];”时布局类的构造函数中的行,然后我收到“EXC_BAD_ACCESS”错误,并且应用程序在尝试解除分配时崩溃。我已经将崩溃追溯到 Layout 类的 dealloc 方法中 someDelegate 对象的释放。如果我留下评论,那么应用程序就可以正常工作。

有人可以解释为什么会发生这种情况,因为它似乎违背了我所读到的关于 Objective-C 内存管理的所有内容。

请注意,代码示例确实有效,但是我的代码没有遵循示例。我的实际 SomeObject 内部是否存在导致自动释放的东西?

提前致谢。

【问题讨论】:

  • 只是出于好奇,如果将“delegate”变量重命名为其他名称会怎样?我知道这无关紧要,但我记得在 cocoa-dev 邮件列表中读到过同样的内容;某些变量名称的奇怪行为。 UIView 有自己的委托,我想知道它是否与此有关。
  • 好主意,但不幸的是我尝试将其重命名为委托,但仍然存在发布问题。
  • 请注意,代码 sn-p 确实有效,所以我认为我的 SomeObject(完整代码而不是 sn-p)中有一些东西导致它自动释放。对象可以在没有被告知的情况下自动释放吗?
  • 许多方法返回自动释放的对象,NSString 的 +stringWith... 和 -stringBy... 方法返回自动释放的对象。您发布的代码肯定符合 Cocoa 内存管理规则,因此请注意代码中的流氓“释放”和“自动释放”方法,并确保保留未由您自己的代码显式分配的所有内容。如果仍然无法正常工作,请提交错误。
  • 问题是一个子类深处的 MutableArray,它是通过工厂创建的(自动发布),但我也在发布。 Zombie 的东西有点帮助,它告诉我一个数组是罪魁祸首,但除此之外没什么。

标签: objective-c memory-management


【解决方案1】:

首先,返回并重新阅读memory management rules,以确保您在其他地方使用委托时没有遗漏任何明显的内容。

接下来,打开 NSZombieEnabled(在您的可执行文件设置中,Arguments 面板中,添加环境变量 NSZombieEnabled 设置为 YES)。

然后在你的 delagate 中添加一个 dealloc 方法(如果它还没有)(确保你调用了 [super dealloc]!)并在那里放置一个断点 - 这会告诉你你的 delagate 何时被释放,这会告诉你什么时候发布。

或者,向您的委托类添加简单的释放/自动释放方法,这些方法除了调用之外什么都不做,然后对它们进行断点,这会告诉您确切的释放时间。

最后三个 cmets:在 Objective C/Cocoa 的标准命名约定中,您应该有小写的参数字段,即应该是:

- (id)initWithFrame:(CGRect)aRect cols:(NSUInteger)Cols rows:(NSUInteger)Rows;

当您的 ivar 和属性名称相同时,很容易意外使用错误的名称,因此我建议使用不同的 ivar 名称和属性名称以避免混淆,可以使用像 Apple 这样的 _ 前缀,也可以使用其他前缀为了避免与 Apple 混淆:

  id<SomeDelegate> _someDelegate;

@synthesize someDelegate = _someDelegate;

Apple 建议不要在 init/dealloc 中使用 setter/getter,所以你的 init 代码应该是:

_someDelegate = [[SomeObject alloc] initWithCols:cols Rows:rows];

【讨论】:

【解决方案2】:

正如 cmets 中提到的,问题似乎不在发布的代码中。

我可以询问更多信息,但我坚定地参加了教人钓鱼营....

-release 中的崩溃通常会产生误导,因为各种优化(通常是尾调用优化)会使崩溃看起来像是发生在实际调用崩溃的一两帧之上。当崩溃发生时,堆栈上没有足够的信息来真正识别罪魁祸首。

当你怀疑你在 -release 或 -dealloc 中有任何形式的崩溃,立即打开 Zombies。这可以通过 Instruments 或通过环境变量或通过在程序执行的早期调用 Foundation 中的函数来完成。

在开发环境附带的文档中搜索“Zombies”或“NSZombie”(这更像是“教人钓鱼”的事情)。

【讨论】:

    【解决方案3】:

    问题是子类深处的 MutableArray,它是通过工厂创建的(自动发布),但我也发布了。不幸的是,崩溃不会表明是哪个继承的 dealloc 导致了崩溃,而只是在第一个被覆盖的 dealloc 处停止。

    Zombie 的事情有点帮助,它告诉我一个数组是罪魁祸首,但除此之外没什么。我认为 NSZombie 还有更多功能,需要更多经验才能充分利用它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-10
      • 2014-08-26
      • 1970-01-01
      • 2011-05-26
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多