【问题标题】:Diagnosing an autorelease error (EXC_BAD_ACCESS)诊断自动释放错误 (EXC_BAD_ACCESS)
【发布时间】:2011-10-01 03:19:28
【问题描述】:

我一直在研究核心数据,并开始编写一些方法来查询不同日期范围的数据。我的核心数据模型非常简单(实体名为 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 实例化时,你使用释放?我没有在代码的其他任何地方明确发布它们,所以幕后肯定有一些事情发生。如果有人能指出我正确的方向,那就太好了!

这里是涉及到的其他方法,因为问题肯定在这些方法中的某个地方。我认为这与我如何传递指向日期的指针有关?

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

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

【问题讨论】:

  • 当我遇到自动释放问题(产生令人沮丧的调试信息)时,我设法通过在代码块周围创建和排出自动释放池来缩小问题(注意保留任何必须在drain 中持续存在)。你最终会找到一个小区域,你的游泳池会引发问题。
  • 我能够将其缩小到 retrieveSmokesForUnit 中的 [beginDate release] 和 [endDate release] 行...如果我删除它们,它运行良好,没有崩溃...
  • 啊,添加代码后问题就很明显了。您创建(保留的)日期对象,然后用 rangeForUnit 中的自动发布版本覆盖它们,然后释放自动发布的版本(让原始日期对象泄漏,顺便说一句)。
  • [calendar rangeOfUnit:unit startDate:&amp;*startDate 中的 & 不是错了吗? rangeOfUnit 不修改 startDate。
  • @ott 那&amp;* 最终成为无操作并且可以被删除。

标签: iphone objective-c memory-management


【解决方案1】:

在:

- (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. 您通过+alloc 创建了一个新的NSDate 对象,因此您拥有它。 beginDate 指向这个新对象;

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

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

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

总结:

  • 您不应该为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;
}

【讨论】:

  • 您的精彩讲解让我离理解Obj-C内存管理又近了一步。非常感谢!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-08
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多