难以为OS X应用程序的内存泄漏

时间:2011-06-29 13:38:44

标签: objective-c xcode macos memory-management

好的,我的内存管理问题让我陷入困境。有一次,我发誓这没有问题,但现在到处都在泄漏记忆,我无法弄清楚原因。

首先,我正在启动NSTask,然后在任务运行时运行循环。

 NSTask *encodingTask = [[NSTask alloc] init];
            NSFileHandle *taskStdout = [NSFileHandle fileHandleForWritingAtPath:encodingOutput];
            [encodingTask setStandardOutput:taskStdout];
            [encodingTask setStandardError:taskStdout];
NSString argString = [NSString stingWithString: @"some arguments"];
[encodingTask setArguments:taskArgs];
            [encodingTask setLaunchPath:somePath];
            [encodingTask launch];

while ([encodingTask isRunning]){
                sleep(1);
                [self encodeProgressTimer];
            }

encodeProgessTimer方法从stdOut抓取最后一行并将其放在菜单栏中:

- (void)encodeProgressTimer
{
    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"menuProgress"]) {
    // Read the last line
 NSString *fileData = [NSString stringWithContentsOfFile:encodingOutput encoding:NSASCIIStringEncoding error:nil];
    NSArray *lines = [fileData componentsSeparatedByString:@"\r"];
    NSString *lastLine = [lines objectAtIndex:[lines count] - 1];
    NSString *percent;
    NSString *eta;
    BOOL dataFound = NO;
    if ([lastLine length] == 71) {
        dataFound = YES;
        percentRange = (NSRange) {23,5};
        etaRange = (NSRange) {61,9};
        percent = [lastLine substringWithRange:percentRange];
        eta = [lastLine substringWithRange:etaRange];
    }
    else if ([lastLine length] == 72) {
        dataFound = YES;
        percentRange = (NSRange) {23,5};
        etaRange = (NSRange) {62,9};
        percent = [lastLine substringWithRange:percentRange];
        eta = [lastLine substringWithRange:etaRange];
    }
    else if ([lastLine length] == 70) {
        dataFound = YES;
        percentRange = (NSRange) {23,5};
        etaRange = (NSRange) {60,9};
        percent = [lastLine substringWithRange:percentRange];
        eta = [lastLine substringWithRange:etaRange];
    }

    if (dataFound) {
        NSMutableString *bottomStr = [[NSMutableString alloc] 
                                  initWithFormat:@"Encoding: %@%% - ETA %@", percent, eta];
                [appDelegate setMenuTop:topString andBottom:bottomStr];
        [bottomStr release];
    }

}

}

我的理解是,当方法完成时,任何我没有专门分配和初始化的东西都应该自动释放,但事实并非如此。调用此内存时,内存使用率每秒都会以指数方式上升。如果我查看我的记忆分配,生活的CFstings的数量会通过屋顶。如果我转向encodeProgressTimer,我的问题就会消失。我尝试将一个自动释放池添加到encodeProgressTimer,这使得内存使用非常稳定,但是在运行20分钟左右后,我得到了一个EXC_BAD_ACCESS。打开僵尸会把它变成:

*** -[NSConcreteAttributedString _drawCenteredVerticallyInRect:scrollable:]: message sent to deallocated instance 0x2bc756e0

我实际上经历了并将每个变量声明更改为它的alloc / init对应物并手动释放它们,但这也没有解决问题。在这一点上,我很难过。

同样为了完整起见,[appDelegate setMenuTop:andBottom:]方法如下所示:

-(void) setMenuTop: (NSString *) top andBottom: (NSString *) bottom
{  
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"menuProgress"]) {
    [statusItem setImage:nil];
    NSMutableParagraphStyle *lineHeight = [[NSMutableParagraphStyle alloc] init];
    [lineHeight setMaximumLineHeight:10.5];
    [lineHeight setLineBreakMode:NSLineBreakByTruncatingMiddle];
    OperationQueue *opQueue = [OperationQueue sharedQueue];
    NSString *sBuffer = [[NSMutableString alloc] initWithFormat: @"%@ (%i More)\n%@", top, [opQueue queueCount] - 1, bottom];
    attributes = [[NSDictionary alloc] initWithObjectsAndKeys:[NSFont menuFontOfSize:9], NSFontAttributeName, lineHeight, NSParagraphStyleAttributeName, nil];
    if (statusTitle)
        [statusTitle release];
    statusTitle = [[NSAttributedString alloc] initWithString: sBuffer  attributes: attributes];
    [statusItem setAttributedTitle: statusTitle];
    [lineHeight release];
    [sBuffer release];
    [attributes release];
    }

}

2 个答案:

答案 0 :(得分:1)

自动释放池中会有大量内容,但是你需要明确地将其耗尽以使内存消失。按如下方式更改while循环:

while ([encodingTask isRunning]){
   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
   sleep(1);
   [self encodeProgressTimer];
   [pool drain];
}

其他内容:如果您在线程上运行此操作,则无法直接更新用户界面项。您需要使用类似performSelectorOnMainThread:的内容来实际更新UI。如果您没有在线程上运行它,则需要重新考虑您的设计。在循环运行时,应用程序的整个UI都将冻结。

答案 1 :(得分:0)

您可能希望在此处使用属性或忽略引用。

if (statusTitle) {
        [statusTitle release];
        statusTitle = nil;
}