Остановился на утечке памяти для приложения OS X - PullRequest
0 голосов
/ 29 июня 2011

ОК, у меня проблема с управлением памятью, которая приводит меня к стене. В какой-то момент я клянусь, что это сработало без проблем, но теперь это повсеместная утечка памяти, и я не могу понять, почему.

Для начала я запускаю 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 ]

1 голос
/ 29 июня 2011

В пуле авто-выпусков будет множество вещей, но вам нужно явно истощить их, чтобы память ушла. Измените цикл while следующим образом:

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

Другие вещи: если вы запускаете это в потоке, вы не можете напрямую обновлять элементы пользовательского интерфейса. Вам нужно использовать что-то вроде performSelectorOnMainThread:, чтобы обновить пользовательский интерфейс. Если вы не запускаете это в потоке, вам нужно переосмыслить свой дизайн. Весь пользовательский интерфейс вашего приложения будет зависать во время работы цикла.

0 голосов
/ 29 июня 2011

Вы можете использовать свойства здесь или обнулить ссылку.

if (statusTitle) {
        [statusTitle release];
        statusTitle = nil;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...