上下文:我有一个使用 GCD 的 iOS 游戏应用程序。对于应用程序,我有三个队列:主队列、游戏逻辑队列(自定义串行)、物理队列(自定义串行)。Physics Queue 用于进行物理模拟,Game Queue 用于进行游戏逻辑。因此,对于每次更新(每 1/60 秒),每个队列都会执行其各自的工作,然后通过在其他队列上调度块来与其他队列共享它。
问题:
使用 GCD:当我玩游戏关卡时,即队列正在做一些工作,我看到我的堆/分配非常快速地增长,这导致应用程序由于内存问题而崩溃。如果我退出关卡并进入非游戏视图,即队列没有做任何工作,内存会慢慢下降(大约需要 2 分钟)并变得稳定。在附图中,图像中的高峰就在我退出游戏关卡并出来之前。之后,随着对象被释放,内存会稳步下降。
没有 GCD:如果我禁用其他两个队列并在主队列上运行所有内容,即我从代码中消除所有并发,我看不到任何显着的堆增长并且游戏运行良好。
已经在互联网上学习/尝试/研究过: 我对块捕获和复制到堆的块的概念有一个简要的了解,但不太确定。据我了解,我一直无法在我的代码中找到任何这样的对象,因为当我退出游戏关卡并进入非游戏视图时,所有预期被解除分配的对象都被解除分配。
问题:
- 带有 GCD 的 App 会创建很多块。创建很多块是一个好习惯吗?
- 在运行仪器时,我发现快速分配但未释放的对象属于 Malloc 48 类别。这些对象的责任库是 libsystem_blocks.dylib,责任调用者是 _Block_copy_internal。一旦我退出游戏关卡,即当队列停止执行任何工作时,这些对象就会慢慢被释放。但是,释放非常慢,需要大约 2 分钟才能完全清理。有什么办法可以加快清理速度?我的怀疑是对象不断堆积,然后导致内存崩溃。
关于可能发生的事情的任何想法?
提前致谢。
根据以下评论的建议,我编写了以下测试代码。我基本上安排了来自 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 相同的堆增长。但令人惊讶的是,在这段代码中,内存从未回落到其初始水平。仪器输出如下:
这段代码有什么问题?任何想法都会有所帮助。