如何解决NSFilehandle上的内存泄漏问题

时间:2013-07-16 14:20:14

标签: objective-c macos memory-leaks

我正在开发一个OS X应用程序,它始终运行并处理其队列中的所有内容。这些作业将其进程记录在stdout中。我试图通过在后台线程中运行的while循环实时获取stdout实时信息。问题是在运行12小时后,内存使用量从25MB增加到> 750MB。该应用程序正在使用ARC,所以我在这里有点迷失。任何人都有任何线索如何解决这个问题?

NSFileHandle *fileStd = [stdPipe fileHandleForReading];

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: cmd];
[task setArguments: arguments];

NSPipe *stdPipe = [NSPipe pipe];
NSPipe *errPipe = [NSPipe pipe];

[task setStandardOutput: stdPipe];
[task setStandardError: errPipe];

NSFileHandle *fileStd = [stdPipe fileHandleForReading];

[task launch];

NSData *readData;
NSString *logString;

while ((readData = [fileStd availableData]) && [readData length]){

   logString = [[NSString alloc] initWithData: readData encoding: NSUTF8StringEncoding];
        [self parseLog:logString];
    }

    readData = nil;
}

[task waitUntilExit];

和parseLog方法:

+ (void) parseLog:(NSString *)output {

    NSArray *chunks = [output componentsSeparatedByString: @"\r"];
    NSString *lastLine = [chunks lastObject];

    [lastLine writeToFile:@"/tmp/status.log" atomically:YES encoding:NSUTF8StringEncoding error:nil];

    NSRange textRange = [lastLine rangeOfString:@"<TEST> "];
    if(textRange.location != NSNotFound) {

        NSMutableDictionary *info = [NSMutableDictionary dictionary];
        [info setObject:lastLine forKey:@"test"];

        [[NSNotificationCenter defaultCenter] postNotificationName:@"handleNotification" object:nil userInfo:info];
    }
}

2 个答案:

答案 0 :(得分:3)

使用分配工具查看堆增长的原因。

特别是,您可以使用快照分析来非常有效地找出这些问题。

Here is a write-up.

简而言之:

  • 在分配工具下启动您的应用
  • (1)按标记堆按钮
  • 等一会儿
  • 转到(1)

重复一段时间。在您的情况下,您可能希望每20分钟到一小时按标记堆按钮一次。


杰里米几乎把它弄好了。你需要重新调整一下你的循环:

while (1) {
@autoreleasepool {
   if (readData = [fileStd availableData]) && [readData length]){

   logString = [[NSString alloc] initWithData: readData encoding: NSUTF8StringEncoding];
        [self parseLog:logString];
    }

    readData = nil;
   } else {
    break;
   }
}

readData需要在游泳池中。

答案 1 :(得分:3)

ARC仍然使用引擎计数“引擎盖下”。特别是,它仍然使用自动释放池,你无法排出任何自动释放池。尝试使用@autoreleasepool块封闭while循环体。

while ((readData = [fileStd availableData]) && [readData length]){
    @autoreleasepool
    {

       logString = [[NSString alloc] initWithData: readData encoding: NSUTF8StringEncoding];
       [self parseLog:logString];
    }
}

这就是在循环开始时创建自动释放池并在结束时将其耗尽的现代等价物。

修改

正如bbum所说,readData在每次迭代时都放在一个自动释放池中,但它在@autoreleasepool位之外,所以代码需要重组:

bool dataToProcess = true;
while (dataToProcess){
    @autoreleasepool
    {
       NSData* readData = [fileStd availableData];
       dataToProcess = [readData length] > 0;
       if (dataToProcess)
       {
           logString = [[NSString alloc] initWithData: readData encoding: NSUTF8StringEncoding];
           [self parseLog:logString];
       }
    }
}