【问题标题】:Objective-C block not being released for background-only applications没有为纯后台应用程序发布 Objective-C 块
【发布时间】:2011-04-03 16:42:48
【问题描述】:

我有一个仅在后台运行的应用程序(通过在 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 可以观察到内存消耗猛增。)

【问题讨论】:

  • 您是否尝试过使用 C 函数 (libDispatch) 而不是 NSOperations?
  • 我认为它会产生相同的结果,因为 NSBlockOperation 被记录为在内部使用 libDispatch。感谢您的建议,无论如何,我会尽快试一试。

标签: objective-c memory-leaks user-input objective-c-blocks background-application


【解决方案1】:

看起来您正在使用基于堆栈的块,该块在超出范围后使用。该块需要被复制。如果改成这样,代码应该可以工作:

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

【讨论】:

  • 感谢您的建议。不过,我很确定,当我使用 addOperationWithBlock 时包装块的 NSBlockOperation 正在负责将块传输到适当的存储。我的问题不在于该块不执行或行为异常;我的问题是,如果应用程序没有 UI,即使它已被放入自动释放池,该块仍会继续占用内存。我仍在尝试,但似乎@Catfish_Man 的回答恰到好处。
【解决方案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];
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-12
    • 2021-10-03
    • 2012-03-20
    • 1970-01-01
    • 2011-06-02
    • 2014-11-17
    • 1970-01-01
    相关资源
    最近更新 更多