Блок Objective C не выпускается для фоновых приложений - PullRequest
3 голосов
/ 25 августа 2010

У меня есть приложение, которое работает только в фоновом режиме (указав LSBackgroundOnly в файле info.plist). Проблема в том, что все блоки, которые я запускаю в параллельных очередях, не освобождаются. Код выполняется в среде с управлением памятью - GC не задействован.

(упрощенный) код выглядит ниже. Blubber - это всего лишь фиктивный класс, который содержит NSDate для тестирования. Кроме того, он перезаписывает retain, release и dealloc для ведения журнала:

NSOperationQueue *concurrentQueue = [[NSOperationQueue alloc] init];
[concurrentQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];

Blubber *aBlubber = [[Blubber alloc] init]; 
aBlubber.aDate = [NSDate date];

[concurrentQueue addOperationWithBlock:^{       
NSAutoreleasePool *blockPool = [[NSAutoreleasePool alloc] init];
    NSDate *test = [aBlubber aDate];
    NSLog(@"Block DONE");
    [blockPool release];    
}];

[aBlubber release];

[concurrentQueue release];

Если я изменю приложение на обычное (то есть не фоновое), я могу наблюдать освобождение блоков всякий раз, когда через пользовательский интерфейс делается какой-либо ввод (даже достаточно изменить фокус на другое окно). Поскольку мое приложение backgorund получает входные данные непосредственно через драйвер HID USB и не имеет окна или строки меню, этого не происходит.

Есть ли способ вручную принудительно запустить цикл запуска или что-то еще, отвечающее за сообщение очередям об освобождении готовых блоков?

(Все другие объекты, которые были сохранены блоками, также не освобождаются, создавая огромные утечки памяти. Эти утечки не могут быть обнаружены инструментами Утечки или ObjectAllocations, но потребление памяти можно наблюдать стремительно, используя top.)

Ответы [ 2 ]

2 голосов
/ 27 января 2011

Одним из распространенных «уловок» для пулов автоматического выпуска является то, что если приложение наращивает память без получения событий, внешний пул (тот, который управляется циклом событий) не будет сливаться.*

Я не думаю, что это применимо здесь, так как вы управляете своим собственным пулом ... но на всякий случай, вы можете попробовать это:

...
//When no events are coming in (i.e. the user is away from their computer), the runloop doesn't iterate, and we accumulate autoreleased objects
[[NSTimer scheduledTimerWithTimeInterval:60.0f target:self selector:@selector(kickRunLoop:) userInfo:nil repeats:YES] retain];
...
- (void) kickRunLoop:(NSTimer *)dummy
{
// Send a fake event to wake the loop up.
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
                                    location:NSMakePoint(0,0)
                               modifierFlags:0
                                   timestamp:0
                                windowNumber:0
                                     context:NULL
                                     subtype:0
                                       data1:0
                                       data2:0]
         atStart:NO];
}
0 голосов
/ 28 февраля 2011

Похоже, вы используете блок на основе стека, который используется после того, как блок вышел из области видимости. Блок должен быть скопирован. Код должен работать, если он изменен на это:

[concurrentQueue addOperationWithBlock:[[^{       
    NSAutoreleasePool *blockPool = [[NSAutoreleasePool alloc] init];
    NSDate *test = [aBlubber aDate];
    NSLog(@"Block DONE");
    [blockPool release];    
}copy]autorelease]];

Посмотрите на этот пост для полной рецензии на блоки: http://gkoreman.com/blog/2011/02/27/blocks-in-c-and-objective-c/

...