Question

I have an application that runs in the background only (by specifying LSBackgroundOnly in the info.plist file). The problem is, that all blocks that I run on parallel queues are not being released. The code is executed in a memory-managed environment - no GC is involved.

The (simplified) code looks like below. Blubber is just some dummy class that holds an NSDate for testing. Also, it overwrites retain, release, and dealloc to do some logging:

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];

If I change the application to be a normal (i.e. non-backgound) application, I can observe the blocks being released whenever any input is made via the UI (even changing the focus to another window is sufficient). Since my backgorund app receives input directly over the HID USB driver and it does not have a window or menu bar this does not happen.

Is there any way to manually force the runloop or whatever is responsible to telling the queues to release the finished blocks?

(All other objects that had been retained by the blocks are also not released, creating huge memory leaks. These leaks cannot be spottet by the Leaks or ObjectAllocations tools but the memory consumption can be observed skyrocketing using top.)

Was it helpful?

Solution

One common "gotcha" for autorelease pools is that if the app is building up memory without receiving events, the outermost pool (the one managed by the event loop) won't be draining.

I don't think that should apply here since you're managing your own pool... but just in case, you could try this:

...
//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];
}

OTHER TIPS

It looks like you are using a stack based block that is used after the block has gone out of scope. The block needs to be copied. The code should work if it is changed to this:

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

Take a look at this post for a full writeup on blocks: http://gkoreman.com/blog/2011/02/27/blocks-in-c-and-objective-c/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top