Блок Objective-C не выпускается для фоновых приложений
У меня есть приложение, которое работает только в фоновом режиме (указав 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, и у него нет окна или строки меню, этого не происходит.
Есть ли способ вручную принудительно запустить цикл запуска или что-то еще, ответственное за указание очередям освободить законченные блоки?
(Все другие объекты, которые были сохранены блоками, также не освобождаются, создавая огромные утечки памяти. Эти утечки не могут быть обнаружены инструментами Leaks или ObjectAllocations, но потребление памяти можно наблюдать стремительно, используя top.)
2 ответа
Одна распространенная "ошибка" для пулов автоматического выпуска состоит в том, что если приложение наращивает память без получения событий, то внешний пул (управляемый циклом событий) не будет исчерпан.
Я не думаю, что это применимо здесь, так как вы управляете своим собственным пулом... но на всякий случай, вы можете попробовать это:
...
//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];
}
Похоже, вы используете блок на основе стека, который используется после того, как блок вышел из области видимости. Блок должен быть скопирован. Код должен работать, если он изменен на это:
[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/