当我在“大”(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
答案 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
附加这些字符串。