【问题标题】:Pesky leak in setter/getter methodssetter/getter 方法中的讨厌泄漏
【发布时间】:2011-07-10 21:13:44
【问题描述】:

似乎我一直在问同样的问题,与记忆有关。我当前的代码完全按照我的预期工作,但我不知道为什么我在 Instruments 中显示了泄漏。

-(NSDate *)startTimeAndDate {
    NSDate *dateToReturn = nil;
    if (startTimeAndDate != nil) {
        dateToReturn = [startTimeAndDate retain];
    } else { //is currently nil, this will be the initial setting
        //return default time if we have a working date
        if (finishTimeAndDate != nil) {
            dateToReturn = [[self dateFromDate:finishTimeAndDate withNewTime:defaultStartTime]retain];
        } else {
            //return the default time with today's date if we have nothing set as yet
            dateToReturn = [[self dateFromDate:[NSDate date] withNewTime:defaultStartTime] retain];
        }
        //save the initial setting
        self.initialStartDateAndTime = [[dateToReturn copy] autorelease];
    }
    [startTimeAndDate release];
    startTimeAndDate = dateToReturn;
    return startTimeAndDate;
}


-(void)setStartTimeAndDate:(NSDate *)inStartTimeAndDate {

    BOOL initialAssignment = NO;
    if (startTimeAndDate == nil) {
        initialAssignment = YES;
    }

    if (startTimeAndDate != inStartTimeAndDate) { //skip everything if passed object is same as current
        //check that the start time is prior to finish only if finish time has been entered
        NSDate *dateToSetStartTo = nil;
        if (finishTimeAndDate != nil) {
            if ([inStartTimeAndDate earlierDate:finishTimeAndDate] == inStartTimeAndDate) {
                // use the new time, it is earlier than current finish time
                dateToSetStartTo = [inStartTimeAndDate retain];
            } else { //start time is not earlier then finish time
                // the received entry is invalid, set start time to 1 default interval from finish
                dateToSetStartTo = [[finishTimeAndDate dateByAddingTimeInterval:-self.defaultTimeInterval] retain];
            }
        } else { //finish time is nil
            // use the new time without testing, nothing else is set
            dateToSetStartTo = [inStartTimeAndDate retain];
        }
        [startTimeAndDate release];
        startTimeAndDate = dateToSetStartTo;
    }
    if (initialAssignment) {
        self.initialStartDateAndTime = [[self.startTimeAndDate copy] autorelease];
    }
}

据我所知,我正在平衡所有保留与releaseautorelease。泄漏似乎仅在第一次通过时引起。我有一个视图控制器,它创建我的模型(此代码所在的位置)并设置开始日期,此时没有做任何其他事情。如果我此时关闭该视图控制器,Instruments 会显示我将日期对象作为泄漏。

我放置了一个NSLog 以在dealloc 显示保留计数,果然,在调用我的最终版本之前它的保留计数为2,当它应该被销毁时保留计数为1。无论我是在初始化后立即关闭还是设置并获取一百次,它总是相同的。 retainCount 在我最后一次打电话给 release 之前是 2 dealloc

我整个周末都在看这个,不知道我哪里出错了。

为了澄清,最初的调用是设置startTimeAndDate 属性。那时所有其他字段都为零,如果不是对象,则为 0。 startTimeAndDate 对象似乎是泄漏对象。

【问题讨论】:

  • 请正确缩进您的代码,并告诉我们泄漏的位置。
  • 如果不清楚,我深表歉意。到目前为止,我只发布了几次,格式对我来说是新的。据我在浏览器中可以看出,它看起来是正确缩进的。我相信泄漏是 NSDate 对象“startTimeAndDate”。
  • 谢谢,现在好多了。出于某种原因,这里的代码块对标签很有趣——在发布时使用空格会更好。
  • 您的代码中有异常多的保留。复制/自动释放的东西看起来不对。在您的 .h 文件中,您可以指定应保留的属性,这将消除此处显示的大部分代码。祝你好运。
  • 我确实设置了要保留的属性,但是由于我同时覆盖了 getter 和 setter,我希望我必须从那里返回或创建一个保留的对象。我已经在没有它们的情况下尝试过,但我确实遇到了异常,调用了 dealloc'd 对象。

标签: objective-c ios cocoa-touch


【解决方案1】:

首先,您能描述一下您试图用这段代码解决的问题吗?我问是因为它看起来很复杂,我最初的想法是简化不仅可以澄清您在做什么,而且还可能解决您的泄漏问题。

其次,(我可能有这个错误),如果您希望这些对象存在于方法范围之外,或者您希望它们可能被您所使用的某些代码释放,您只需要保留/释放对象坚持你的方法。基于此,您似乎在代码中过度保留和释放。我认为您可以删除很多内容。

再次,我可能是错的,但看起来你确实会泄漏。我认为是这样的原因是 - 在您的第一次通过时,您会在 dateToReturn 中保留一些数据,这是一个局部变量。那你就做

self.initialStartDateAndTime = [[dateToReturn copy] autorelease];

但这不是发布dateToReturn。相反,它正在发布dateToReturn 的副本。 dateToReturn 仍然保留。假设您打算自动释放副本,因为initialStartDateAndTime 设置了保留,我认为您应该这样做:

self.initialStartDateAndTime = [[dateToReturn copy] autorelease];
[dateToReturn release];

当然,如果您删除了额外的保留/释放,那么这将再次变得更简单。

我建议的最后一件事是关于命名。像这样的代码的问题是你有许多方法和变量,它们的名字都非常相似。这可能使其难以遵循并导致错误。所以问问自己你是否真的需要这么多变量。您能否通过更改一些名称使您的代码更具可读性。

【讨论】:

  • startTimeAndDate 是保留属性。我重写了 setter 和 getter 以验证和提供默认值。验证规则和默认值会根据其他一些属性的状态而变化,例如是否输入了完成时间、是否是新记录或正在编辑等。initialStartTimeAndDate 是另一个具有标准 setter 和 getter 的保留属性@合成。它意味着与 dateToReturn/startTimeAndDate 完全分开,因此是副本。
  • 我不想发布 dateToReturn ,因为我最终将 startTimeAndDate 设置为保留为该属性中的新对象。
  • 至少在设置 startTimeAndDate 后才需要释放它,否则会泄漏并破坏内存管理规则。
  • 正确吗?在我的二传手中,我将其设置为保留。我假设的是,在 dealloc 方法中,保留通过释放来平衡。如果我使用标准设置器,我会同意,但我自己会覆盖和设置 ivar。
【解决方案2】:

大妈,忽略我说的话。我刚刚再次浏览了代码,你是对的。我认为您基本上被代码的复杂性所困扰。我发现它很难理解,尤其是属性的数量。我认为在这个阶段我会做的是将代码复制到单元测试并从那里运行它。然后你可以更好地测试和调试它。如果您还没有进行单元测试,我会推荐 GHUnit

发生的另一件事是在您的程序的其他地方执行的代码保留了日期。因此触发泄漏。例如,如果 inStartTimeAndDate 的保留计数为 1,但没有被调用 setter 的代码释放,那么您最终可能会得到保留为 2 的 startTimeAndDate

话虽如此,这是我对 getter 的重写,试图澄清发生了什么:

-(NSDate *)startTimeAndDate {

    // If we have it, bail out fast.
    if (startTimeAndDate == nil) {
        return startTimeAndDate;
    }

    // Is currently nil, this will be the initial setting
    NSDate *dateToReturn = nil;
    //return default time if we have a working date
    if (finishTimeAndDate != nil) {
        dateToReturn = [self dateFromDate:finishTimeAndDate withNewTime:defaultStartTime];
    } else {
        //return the default time with today's date if we have nothing set as yet
        dateToReturn = [self dateFromDate:[NSDate date] withNewTime:defaultStartTime];
    }
    //save the initial setting
    self.initialStartDateAndTime = [[dateToReturn copy] autorelease];

    startTimeAndDate = [dateToReturn retain];
    return startTimeAndDate;
}

这次重写的主要原因是,如果有startTimeAndDate,那么代码就是这样做的:

dateToReturn = [startTimeAndDate retain];
...
[startTimeAndDate release];
startTimeAndDate = dateToReturn;

这似乎有点毫无意义,因为它有效地进行了保留、释放和自我分配。它会起作用,但如果我们忽略它,则出现错误的可能性会更小。

【讨论】:

  • 老实说,我认为这可能接近于它的开始方式。我越是试图了解泄漏的来源,这些方法就越复杂。我已经提交了一段时间,现在你给了我一些需要考虑的事情,回来重新审视一下。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2018-12-27
  • 1970-01-01
  • 2011-04-14
  • 2017-09-09
  • 1970-01-01
  • 1970-01-01
  • 2017-08-26
  • 1970-01-01
相关资源
最近更新 更多