Objective-C 块未针对仅后台应用程序发布

发布于 2024-09-16 12:46:19 字数 1086 浏览 16 评论 0原文

我有一个仅在后台运行的应用程序(通过在 info.plist 文件中指定 LSBackgroundOnly )。 问题是,我在并行队列上运行的所有块都没有被释放。 该代码在内存管理环境中执行 - 不涉及 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 观察到内存消耗急剧增加。)

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.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

国粹 2024-09-23 12:46:19

自动释放池的一个常见“问题”是,如果应用程序在没有接收事件的情况下构建内存,那么最外层的池(由事件循环管理的池)将不会耗尽。

我认为这不应该适用于这里,因为您正在管理自己的池......但为了以防万一,您可以尝试以下操作:

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

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];
}
浅语花开 2024-09-23 12:46:19

看起来您正在使用基于堆栈的块,该块在块超出范围后使用。需要复制该块。如果将代码更改为以下内容,则该代码应该可以工作:

[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/

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/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文