3

我有一个仅在后台运行的应用程序(通过LSBackgroundOnly在 info.plist 文件中指定)。问题是,我在并行队列上运行的所有块都没有被释放。代码在内存管理的环境中执行 - 不涉及 GC。

(简化的)代码如下所示。Blubber 只是一个包含 NSDate 用于测试的虚拟类。此外,它会覆盖retainreleasedealloc做一些日志记录:

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

如果我将应用程序更改为普通(即非背景)应用程序,我可以观察到每当通过 UI 进行任何输入时释放的块(即使将焦点更改为另一个窗口就足够了)。由于我的 backgorund 应用程序直接通过 HID USB 驱动程序接收输入,并且它没有窗口或菜单栏,因此不会发生这种情况。

有没有办法手动强制运行循环或任何负责告诉队列释放完成的块?

(所有其他被块保留的对象也没有被释放,造成巨大的内存泄漏。这些泄漏不能被 Leaks 或 ObjectAllocations 工具发现,但使用 top 可以观察到内存消耗猛增。)

4

2 回答 2

2

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];
}
于 2011-01-27T08:21:46.030 回答
0

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/

于 2011-02-27T22:36:08.067 回答