【问题标题】:NSDateFormatter memory leak?NSDateFormatter 内存泄漏?
【发布时间】:2013-07-19 21:00:52
【问题描述】:

NSDateFormatter dateFromString: 方法有问题。有一个带有字符串日期的字典数组,我想将它们转换为NSDates。但是每次我调用这个方法时都会有一个巨大的堆增长(我需要多次调用它,因为它是一个“更新”方法)。我也在后台线程中调用它并且 ARC 已打开。我究竟做错了什么?任何帮助,请。

更新: 函数完整代码:

- (GraphController *) initGraphWithData: (NSArray *) points forInterval: (GraphInterval) interval andFrame: (CGRect) frame gestures: (BOOL) gestures secondGraph: (NSArray *) secondPoints graphNames: (NSArray *) graphNames

{
self = [super init];

_calendar = [NSCalendar currentCalendar];

_secondPoints = secondPoints;
_graphNames = graphNames;

[self.view setFrame:frame];

_points = [[NSMutableArray alloc] init];
double minValue = HUGE_VALF, maxValue = -HUGE_VALF;
NSDate *minDate = [NSDate date], *maxDate = [NSDate dateWithTimeIntervalSince1970:0];
NSMutableDictionary *datesTable = [[NSMutableDictionary alloc] init];          


int index = 0;
for(NSArray *pointArray in points){

    NSDictionary *point = pointArray[0];
    NSMutableDictionary *newPoint = [[NSMutableDictionary alloc] initWithDictionary:point];


    // convert date        
    NSString *dateString = (NSString *)[point objectForKey:@"date"];
    NSLog(@"dateString to convert: %@", dateString);
    [_dateFormatter setDateFormat:@"MM/dd/yyyy"];
    NSDate *date = [_dateFormatter dateFromString: dateString];
    [newPoint setObject: date forKey:@"date"];


    // convert numbers
    [newPoint setObject: [NSNumber numberWithFloat:[((NSString *)[point objectForKey:@"value"]) floatValue]] forKey:@"value"];
    if(secondPoints)
        [newPoint setObject: [NSNumber numberWithFloat:[((NSString *)[secondPoints[index] objectForKey:@"value"]) floatValue]] forKey:@"secondValue"];
    [newPoint setObject: [NSNumber numberWithInt:index]  forKey:@"index"];

    // min and max
    if([[newPoint objectForKey:@"value"] floatValue] < minValue)
        minValue = [[newPoint objectForKey:@"value"] floatValue];
    if([[newPoint objectForKey:@"value"] floatValue] > maxValue)
        maxValue = [[newPoint objectForKey:@"value"] floatValue];

    // check second value
    if(self.secondPoints){            
        if([[newPoint objectForKey:@"secondValue"] floatValue] < minValue)
            minValue = [[newPoint objectForKey:@"secondValue"] floatValue];
        if([[newPoint objectForKey:@"secondValue"] floatValue] > maxValue)
            maxValue = [[newPoint objectForKey:@"secondValue"] floatValue];
    }

    if([[newPoint objectForKey:@"date"] timeIntervalSince1970] > [maxDate timeIntervalSince1970])
        maxDate = [newPoint objectForKey:@"date"];
    if([[newPoint objectForKey:@"date"] timeIntervalSince1970] < [minDate timeIntervalSince1970])
        minDate = [newPoint objectForKey:@"date"];

    [self.points addObject:newPoint];
    [datesTable setObject:newPoint  forKey: [[NSNumber numberWithDouble:[date timeIntervalSince1970]] stringValue] ];

    index++;            

}




// set draw parameters

_drawVars = [[NSMutableDictionary alloc] init];

NSDate *startSearchDate;
switch (interval) {
    case GraphIntervalWeek:
        startSearchDate = [maxDate dateByAddingTimeInterval:-7*24*3600];
        break;
    case GraphIntervalMonth:
        startSearchDate = [maxDate dateByAddingTimeInterval: -31*24*3600];
        break;
    case GraphIntervalSixMonths:
        startSearchDate = [maxDate dateByAddingTimeInterval:-6*31*24*3600];
        break;
    case GraphIntervalYear:
        startSearchDate = [maxDate dateByAddingTimeInterval:-365*24*3600];
        break;
    case GraphIntervalAllTime:
        break;
    default:
        break;
}

NSMutableDictionary *searchPoint;

while(searchPoint == nil && [startSearchDate timeIntervalSince1970] > [minDate timeIntervalSince1970]){
    searchPoint = [datesTable objectForKey:[[NSNumber numberWithDouble:[startSearchDate timeIntervalSince1970]] stringValue]];
    startSearchDate = [startSearchDate dateByAddingTimeInterval:-24*3600];
}

if(searchPoint != nil && interval != GraphIntervalAllTime)
 [self.drawVars setObject:[searchPoint objectForKey:@"index"] forKey:@"startDrawIndex"];
else
 [self.drawVars setObject:[NSNumber numberWithInt:0] forKey:@"startDrawIndex"];

[self.drawVars setObject:[[NSNumber numberWithFloat:minValue] copy] forKey:@"minValue"];
[self.drawVars setObject:[[NSNumber numberWithFloat:maxValue] copy] forKey:@"maxValue"];
[self.drawVars setObject:minDate forKey:@"minDate"];
[self.drawVars setObject:maxDate forKey:@"maxDate"];
[self.drawVars setObject:datesTable forKey:@"datesTable"];



// set drawImageView
_drawArea = [[UIImageView alloc] initWithFrame: frame];
[self.view addSubview:self.drawArea];


// set overlayImageView for fingerInpect
_inspectOverlay = [[UIImageView alloc] initWithFrame:frame];
[self.view addSubview:self.inspectOverlay];

// set hintUIView for fingerInspect
_hint = [[Hint alloc] initWithFrame:frame];
[self.hint initHint];
self.hint.hidden = YES;
[self.drawArea addSubview:self.hint];    



UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panHandler:)];
UILongPressGestureRecognizer *longPressRecognizer  = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressHandler:)];    


if(gestures)
    [self.view addGestureRecognizer:panRecognizer];
[self.view addGestureRecognizer:longPressRecognizer];

return self;
}

【问题讨论】:

  • 您是否感觉到性能问题? (滚动、动画等)
  • @JackyBoy 不,但是大约五次更新后应用程序崩溃
  • 仪器显示正在泄漏的分配行,不一定是直接泄漏的原因。
  • @bbum 这种泄漏的迹象是什么,或者我怎样才能找到它们?

标签: ios objective-c memory-leaks nsdateformatter


【解决方案1】:

我看到您正在使用 Heapshot 分析。好的。以下是更多细节:

http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/

现在,希望您的每一次 Heapshot 迭代都代表在您的应用中执行的操作应该将其返回到基本状态。例如,打开和关闭文档。

如果是这种情况,您的数据显示每次迭代会增长 500K-600K。这是相当多的。

请注意,尝试分析最后一次迭代通常是没有用的;由于应用程序的当前状态,其中许多对象都会存在。你真的想专注于第二或第四次迭代。

关于特定日期或日期格式化程序泄漏的原因,请打开引用计数事件跟踪,然后查看其中一个泄漏对象的保留/释放事件。会有一些额外的保留。

【讨论】:

  • 太棒了!谢谢你的方法。终于发现了我的愚蠢错误。出于某种原因,我为控制器设置了 parentView - 这是额外的保留。
【解决方案2】:

在进入循环之前,您正在做的事情是init NSDateFormatter。如果您希望使用相同的日期格式 (MM/dd/yyyy),您也可以将其放在与 init 相同的位置。可能是您分配了太多对象,所以我建议将所有内容都放在 @autoreleasepool {} 中(又名:自动释放中的循环内容)。

我看到您在 for 中分​​配 *newPoint,然后您将日期设置为该对象。之后做什么?

【讨论】:

  • 然后我将 newPoint 添加到字典和循环末尾的数组中:[self.points addObject:newPoint]; [datesTable setObject:newPoint forKey: [[NSNumber numberWithDouble:[date timeIntervalSince1970]] stringValue] ]; autoreleasepool 没有帮助(
  • 我在代码中有很多小的泄漏,例如在带有 *newPoint 分配的屏幕截图上。我必须寻找那些小物体的容器(控制器)的泄漏来修复所有东西,这是对的吗?
  • 我无法告诉您如何在不了解代码如何完全工作的情况下修复代码。不过,您是对的,应该修复漏洞。
猜你喜欢
  • 2011-12-02
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 2011-10-08
  • 2013-01-20
  • 2011-10-31
相关资源
最近更新 更多