setter / getter方法中的Pesky泄漏

时间:2011-07-10 21:13:45

标签: objective-c ios cocoa-touch

好像我一直在问同样的问题,与记忆有关。我当前的代码完全符合我的意图,但我无法理解为什么我在仪器中显示泄漏。

-(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来平衡所有保留。泄漏似乎仅在第一次通过时引起。我有一个视图控制器,它创建我的模型(其中这个代码所在)并设置一个开始日期,此时没有其他任何事情。如果我在那时关闭那个视图控制器,仪器显示我将日期对象作为泄漏。

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

我整个周末都在看这个,但无法弄清楚我哪里出错了。

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

2 个答案:

答案 0 :(得分:4)

首先,您能描述一下您尝试使用此代码解决的问题吗?我问,因为它看起来非常复杂,我最初的想法是简化不仅会澄清你在做什么,而且也可能解决你的泄漏问题。

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

我可能错了,但看起来你确实会泄漏。我这么认为的原因是 - 在你的第一次传递中,你在dateToReturn中保留了一些本地变量的数据。然后你做

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

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

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

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

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

答案 1 :(得分:2)

大坝,不理我说的话。我刚刚完成了代码,你说得对。我认为你基本上被代码的复杂性所灼烧。我发现很难遵循,特别是对于属性的数量。我想在这个阶段我要做的是将代码复制到单元测试并从那里运行。然后你可以更好地测试和调试它。如果你还没有进行单元测试,我会推荐GHUnit

另一件事是,在程序中的其他地方执行的代码会保留日期。因此引发泄漏。例如,如果inStartTimeAndDate的保留计数为1,但调用setter的代码未释放,那么最终可能会使用startTimeAndDate并保留2。

话虽如此,这是我重写吸气剂以试图澄清最新情况:

-(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;

这似乎有点无意义,因为它有效地进行保留,释放和自我分配。它会起作用,但是如果我们把它遗漏的话就会有更少的机会。