诊断自动释放错误(EXC_BAD_ACCESS)

时间:2011-10-01 03:19:29

标签: iphone objective-c memory-management

我一直在玩核心数据,并开始编写一些方法来查询不同的数据日期范围。我的核心数据模型非常简单(实体名为Smoke,带有一个字段 - 时间戳(日期类型)。

当我执行我的代码时,返回正确的计数,但是我得到一个自动释放错误 - 我使用NSZombies跟踪它到下面的方法:

- (NSUInteger)retrieveSmokesForUnit:(NSCalendarUnit)unit
{
    NSDate *beginDate = [[NSDate alloc] init];
    NSDate *endDate = [[NSDate alloc] init];
    [self rangeForUnit:unit containingDate:[NSDate date] startsAt:&beginDate andEndsAt:&endDate];
    NSInteger count = [self numberOfSmokes:beginDate toDate:endDate];

    [beginDate release];
    [endDate release];

    return count;
}

所以我得到了这个概念 - 我发布的NSDate对象beginDate和endDate次数太多了 - 但为什么会这样呢?我认为规则是当你用alloc实例化时,你使用release?我不会在代码中的任何其他位置显式释放它们,因此必须在幕后发生一些事情。如果有人能指出我正确的方向,那就太好了!

以下是涉及的其他方法,因为问题必须在这些方面。我假设它与我如何传递指向日期的指针有关?

在视图控制器中调用的初始调用

- (IBAction)cigButtonPressed
{
   NSUInteger smokes = [[DataManager sharedDataManager] retrieveSmokesForUnit:NSWeekCalendarUnit];

    NSLog(@"Count test = %i", smokes);
} 

这会调用问题开头的方法,然后调用:

- (NSUInteger)numberOfSmokes:(NSDate *)beginDate toDate:(NSDate *)endDate {

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Smoke" inManagedObjectContext:self.managedObjectContext];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];

    //Create predicate
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(timeStamp >= %@) AND (timeStamp < %@)", beginDate, endDate];

    //Setup request
    [request setEntity:entity];
    [request setPredicate:predicate];

    NSError *error;
    NSUInteger smokes = [self.managedObjectContext countForFetchRequest:request error:&error];
    NSLog(@"Number of smokes retrieved: %d", smokes);
    [request release];
    return smokes;    
}

谢谢!

编辑 - 遗漏了一个相关的方法:

- (void)rangeForUnit:(NSCalendarUnit)unit containingDate:(NSDate *)currentDate startsAt:(NSDate **)startDate andEndsAt:(NSDate **)endDate {

    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

    [calendar rangeOfUnit:unit startDate:&*startDate interval:0 forDate:currentDate];
    *endDate = [calendar dateByAddingComponents:[self offsetComponentOfUnit:unit] toDate:*startDate options:0];
    [calendar release];
}

1 个答案:

答案 0 :(得分:3)

在:

- (void)rangeForUnit:(NSCalendarUnit)unit containingDate:(NSDate *)currentDate startsAt:(NSDate **)startDate andEndsAt:(NSDate **)endDate {

    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

    [calendar rangeOfUnit:unit startDate:&*startDate interval:0 forDate:currentDate];
    *endDate = [calendar dateByAddingComponents:[self offsetComponentOfUnit:unit] toDate:*startDate options:0];
    [calendar release];
}

startDateendDate是输出参数。他们拥有来电者,因此他们应该被释放。

然后,在:

- (NSUInteger)retrieveSmokesForUnit:(NSCalendarUnit)unit
{
    NSDate *beginDate = [[NSDate alloc] init];
    NSDate *endDate = [[NSDate alloc] init];
    [self rangeForUnit:unit containingDate:[NSDate date] startsAt:&beginDate andEndsAt:&endDate];
    NSInteger count = [self numberOfSmokes:beginDate toDate:endDate];

    [beginDate release];
    [endDate release];

    return count;
}

发生以下情况:

  1. 您通过NSDate创建了一个新的+alloc对象,因此您拥有它。 beginDate指向此新对象;

  2. 您通过NSDate创建了一个新的+alloc对象,因此您拥有它。 endDate指向此新对象;

  3. 您发送-rangeUnit:containingDate:startsAt:andEndsAt:,将beginDateendDate的地址作为参数传递。返回时,这两个变量指向方法中放置的任何内容。您拥有相应的对象(参见上文),并且您泄漏了在步骤1和2中创建的两个NSDate对象。

  4. 您将-release发送给beginDateendDate。你不拥有它们,因此你不应该释放它们。

  5. 总结:

    • 你不应该为beginDateendDate创建新对象,因为它们被-rangeUnit…返回。这会导致内存泄漏;

    • 您不应该发布beginDateendDate,因为您不拥有-rangeUnit…返回的对象。这会导致过度发布。

    以下代码应该修复您的泄漏和过度发布:

    - (NSUInteger)retrieveSmokesForUnit:(NSCalendarUnit)unit
    {
        NSDate *beginDate;
        NSDate *endDate;
        [self rangeForUnit:unit containingDate:[NSDate date] startsAt:&beginDate andEndsAt:&endDate];
        NSInteger count = [self numberOfSmokes:beginDate toDate:endDate];
    
        return count;
    }