stringByAppendingString导致系统内存耗尽应用程序

时间:2013-11-19 20:48:20

标签: objective-c

当我在“大”(37000行)文本文件上运行以下代码时,有人能指出我的系统告诉我内存应用程序耗尽的方向吗?

-(void) writeToFile: (NSString*)filePath withSeparator:(NSString*) fieldSep{
NSString* completeFile = [[[NSString alloc] initWithString:@""] autorelease];
for(int i=0;i<[self numberOfRows];i++){
    printf("im at line number... %i of %i\n",i,[self numberOfRows]);
    for(int j=0;j<[self numberOfColumns];j++){
        completeFile = [completeFile stringByAppendingString:[self objectInRow:i column:j]];

        if(j<[self numberOfColumns]-1){
            //separator for all columns except last one
            completeFile = [completeFile stringByAppendingString:fieldSep];
        }
    }
        completeFile = [completeFile stringByAppendingString:@"\n"];
}
NSError *error = nil;
[completeFile writeToFile:filePath atomically:NO
                 encoding:NSStringEncodingConversionAllowLossy error:&error];
if(error){
    NSLog(@"Error writing file at %@\n%@",
          filePath, [error localizedFailureReason]);
}

}

我为调试原因添加了printf,前面似乎有4000行,然后它慢慢减速...我的文件包含超过37000行,类似于:

1893-11-6   136 194 165

3 个答案:

答案 0 :(得分:7)

使用工厂方法分配对象时,会将对象添加到autoreleasepool。自动释放池仅在您的IBAction返回后运行事件循环时耗尽。

这里的技巧是将循环的内容放在自己的autorelasepool中。

但我们先解决最大的问题。你应该在这里使用一个 NSMutableString 类,这将大大减少你需要创建的对象数量。

我们将completeFile切换为 NSMutableString ,使用工厂方法构建,然后追加到它:

-(void) writeToFile: (NSString*)filePath withSeparator:(NSString*) fieldSep{
    NSMutableString* completeFile = [NSMutableString string];
    for(int i=0;i<[self numberOfRows];i++){
        printf("im at line number... %i of %i\n",i,[self numberOfRows]);
        for(int j=0;j<[self numberOfColumns];j++){
            [completeFile appendString:[self objectInRow:i column:j]];

            if(j<[self numberOfColumns]-1){
                //separator for all columns except last one
                completeFile appendString:fieldSep];
            }
        }
            [completeFile appendString:@"\n"];
    }
    NSError *error = nil;
    [completeFile writeToFile:filePath atomically:NO
                     encoding:NSStringEncodingConversionAllowLossy error:&error];
    if(error){
        NSLog(@"Error writing file at %@\n%@",
              filePath, [error localizedFailureReason]);
    }
}

但这留下了另一个问题。看到[self objectInRow:i column:j]?它(可能)仍然是一个自动释放的对象。那不会被清理干净。

我们可能已经让您的代码在没有崩溃的情况下运行,具体取决于数据的大小,但是当 它崩溃时 时会出现的问题。

要解决这个问题,我们需要引入自动释放池。我们每列做一行。这可能看起来过多(事实上,在这种情况下,因为我们已经消除了外循环中的autoreleasepool使用),但自动释放池相当便宜。如果您正在对大量数据进行循环,那么这只是一种很好的做法。

您可以使用@autorelease块替换每个for块,例如:

for(int i=0;i<[self numberOfRows];i++){

使用:

for(int i=0;i<[self numberOfRows];i++) @autoreleasepool {

这给了我们这段代码:

-(void) writeToFile: (NSString*)filePath withSeparator:(NSString*) fieldSep{
    NSMutableString* completeFile = [NSMutableString string];
    for(int i=0;i<[self numberOfRows];i++) @autoreleasepool {
        printf("im at line number... %i of %i\n",i,[self numberOfRows]);
        for(int j=0;j<[self numberOfColumns];j++) @autoreleasepool {
            [completeFile appendString:[self objectInRow:i column:j]];

            if(j<[self numberOfColumns]-1){
                //separator for all columns except last one
                completeFile appendString:fieldSep];
            }
        }
            [completeFile appendString:@"\n"];
    }
    NSError *error = nil;
    [completeFile writeToFile:filePath atomically:NO
                     encoding:NSStringEncodingConversionAllowLossy error:&error];
    if(error){
        NSLog(@"Error writing file at %@\n%@",
              filePath, [error localizedFailureReason]);
    }
}

最后一点。您在此处的错误检查不安全。 成功中传递的错误指针会发生什么变化。

    [completeFile writeToFile:filePath atomically:NO
                     encoding:NSStringEncodingConversionAllowLossy error:&error];
    if(error){
        NSLog(@"Error writing file at %@\n%@",
              filePath, [error localizedFailureReason]);
    }

相反,你想要这个:

    BOOL ok = [completeFile writeToFile:filePath atomically:NO
                     encoding:NSStringEncodingConversionAllowLossy error:&error];
    if(!ok){
        NSLog(@"Error writing file at %@\n%@",
              filePath, [error localizedFailureReason]);
    }

然后,这应该做你想要的。

答案 1 :(得分:4)

问题是stringByAppendingString:的每次调用都会创建一个新的自动释放的NSString对象。但是,由于该方法在循环中继续,自动释放不会有机会释放这些对象。

你可以通过在内循环周围添加一个自动释放池来解决它,如下所示:

for(int i=0;i<[self numberOfRows];i++){
    printf("im at line number... %i of %i\n",i,[self numberOfRows]);
    @autoreleasepool {
        for(int j=0;j<[self numberOfColumns];j++){
            completeFile = [completeFile stringByAppendingString:[self objectInRow:i column:j]];

            if(j<[self numberOfColumns]-1){
                //separator for all columns except last one
                completeFile = [completeFile stringByAppendingString:fieldSep];
            }
        }
    }
        completeFile = [completeFile stringByAppendingString:@"\n"];
}

答案 2 :(得分:1)

您应该使用NSMutableString附加这些字符串。