上下文:我有一个使用GCD的iOS游戏应用程序。对于应用程序,我有三个队列:主队列,游戏逻辑队列(自定义序列),物理队列(自定义序列)。 Physics Queue用于进行物理模拟,Game Queue用于进行游戏逻辑。因此,对于每次更新(每1/60秒),每个队列执行其各自的工作,然后通过调度其他队列上的块与其他队列共享。
问题:
使用GCD:当我玩游戏级别,即队列正在做一些工作时,我看到我的堆/分配增长非常快,导致应用程序因内存问题而崩溃。如果我退出关卡并进入非游戏视图,即队列没有做任何工作,则内存会慢慢下降(大约需要2分钟)并变得稳定。在附图中,图像中的峰值就在我退出游戏关卡并出现之前。之后,当对象被解除分配时,内存会稳定下降。
没有GCD:如果我禁用其他两个队列并运行主队列上的所有内容,即我从代码中消除所有并发,我没有看到任何显着的堆增长,游戏运行正常
已在互联网上研究/尝试/研究过: 我简要了解了块捕获的概念以及被复制到堆中的块但是不太确定。据我所知,我无法在我的代码中找到任何此类对象,因为当我退出游戏级别并进入非游戏视图时,所有预期要解除分配的对象都将被释放。
问题:
关于可能发生的事情的任何想法?
提前致谢。
根据以下评论中的建议,我编写了以下测试代码。我基本上安排了从CADisplayLink回拨,然后在回调中我安排了5000个块到自定义队列。
// In a simple bare-bones view controller template I wrote the following code
- (void)viewDidLoad
{
[super viewDidLoad];
self.objDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(loop:)];
[self.objDisplayLink setFrameInterval:1/60];
[self.objDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)loop:(CADisplayLink*)lobjDisplayLink
{
static int lintNumBlocks = 0;
if (lintNumBlocks < 5000)
{
dispatch_async(self.testQueueTwo,
^{
@autoreleasepool
{
NSLog(@"Block Number ; %d", lintNumBlocks);
int outerIndex = 1000;
while (outerIndex--)
{
NSLog(@"Printing (%d, %d)", outerIndex, lintNumBlocks);
}
dispatch_async(dispatch_get_main_queue(),
^{
@autoreleasepool
{
NSString* lstrString = [NSString stringWithFormat:@"Finished Block %d", lintNumBlocks];
self.objDisplayLabel.text = lstrString;
}
});
}
});
lintNumBlocks++;
}
else
{
self.objDisplayLabel.text = @"Finished Running all blocks";
[self.objDisplayLink invalidate];
}
}
此代码还提供了与前一篇文章中提到的GCD相同的堆增长。但令人惊讶的是,在这段代码中,内存永远不会降回到初始级别。仪器输出如下:
这段代码有什么问题?任何想法都会有所帮助。
答案 0 :(得分:3)
当我每隔60秒触发一次CADisplayLink时,我在GCD队列周围看到了同样的内存堆积,但帧渲染块的耗时要长于完成。块会堆积在队列中,正如您所看到的那样,它们会产生一些与之相关的开销。
迈克·阿什有a great writeup about this,他在那里展示了建立加工区块的后果,以及减轻这些压力的方法。此外,最近一次关于GCD的WWDC会议以及如何在仪器中诊断这一点已经涵盖了这一点,但我现在找不到具体的会议。在我的情况下,我最终使用类似于Mike所得的东西,并且我使用调度信号量来防止在内存中积累块。我在this answer中描述了这种方法,以及代码。我所做的是使用最大计数为1的信号量,然后在调度新块之前检查它。如果那个类型的另一个块坐在串行队列上,我保释并且不会在堆上扔另一个。块执行完毕后,我会减少信号量计数,以便添加另一个。
听起来你需要这样的东西来管理队列中新块的添加,因为你希望能够在游戏中增加负载时丢帧。
答案 1 :(得分:0)
主队列有一个自动释放池,在runloop的每次迭代中耗尽
并发队列没有想到..至少NSThreads默认不
将您的代码包装在@autoreleasepool
中的队列中答案 2 :(得分:0)
尝试使用dispatch_async_f而不是dispatch_async。它避免了块复制。