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