0

我正在尝试从磁盘读取文件,将其按行分隔,将其存储到 中NSArray,然后遍历该数组并对数据执行一些非常基本的操作。但是,如果我使用的是一个很小的文件(大约 5000 行数据,每行一个字),我最终会在循环结束时使用几百兆字节的内存。如果我使用一个非常大的文件(约 200,000 行),我最终会使用几 GB 的内存!为什么会这样?当我将文件加载到数组中时,我应该会看到内存的初始峰值,但是内存使用应该保持相当稳定。我正在使用 ARC,但我不相信它,所以现在我自己做所有的内存管理,我确信我正在释放我在循环中分配的所有临时内容。

NSString *fileContents = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

NSScanner *scanner = [NSScanner scannerWithString:fileContents];
NSMutableArray *wordList = [[[NSMutableArray alloc] init] autorelease];
while (![scanner isAtEnd]) {
    NSString *line = [[NSString alloc] init];
    [scanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] intoString:&line];
    [wordList addObject:line];
    [line release];
}
return wordList;
4

2 回答 2

4

我看到两个问题;您不必要地创建了一个字符串,并且应该将字符集的创建从循环中提升出来。扫描仪可能也在创建临时对象;您还应该在循环中添加一个内部自动释放池。

NSMutableArray *wordList = [[[NSMutableArray alloc] init] autorelease];
NSCharacterSet * newline = [NSCharacterSet newlineCharacterSet];
while (![scanner isAtEnd]) {
    @autoreleasepool{
        NSString *line;
        [scanner scanUpToCharactersFromSet:newline intoString:&line];
        [wordList addObject:line];
    }
}

该字符串只是一个普通的旧泄漏;scanUpToCharactersFromSet:intoString:不要求分配的字符串,并且在那之后您已经失去了对它的引用 -release不是针对与您分配的实例相同的实例。然而,Richard 是对的,这里分配的内存量并不太重要。

至于字符集,自动释放池在你的循环过程中不会被耗尽,所以如果newlineCharacterSet每次调用它时都会创建和自动释放一个新实例,你也会在那里建立内存。我希望它NSCharacterSet足够聪明,可以为您缓存它,但在String Programming Guide中有一条注释:

  • 缓存字符集(也许在全局字典中)而不是不断地重新创建它们。

表明情况可能并非如此。

然而,我的三个建议中最有效的一个可能是内部自动释放池。

最后,使用ARC;它可以工作,并且可以处理字符串和字符集的问题。

于 2013-01-23T18:22:42.303 回答
0

您必须考虑到在这段代码中有很多事情发生的事实。

首先,因为您使用的是 NSObjects,所以可能有很多开销,用于存储指向这些对象的指针、存储它们的函数和虚函数表等内容。除此之外,您还有 10,000 个以 null 结尾的字符串。你还有一个 NSMutableArray,根据它的实现方式,它最多可以有一半的分配空间是空的,以允许添加额外的元素,而不是在每次有新元素时增加 NSMutableArray 的大小。

如果您真的想让代码更有效地使用内存,您可能需要考虑使用字符数组来存储字符串和char *数组来存储单词列表。我还建议遵循关于使用 Instruments 的“Catfish_Man”建议。

希望这可以帮助!祝你好运!

于 2013-01-23T18:42:05.743 回答