コンテキスト: GCD を使用する iOS ゲーム アプリケーションがあります。アプリケーションには、メイン キュー、ゲーム ロジック キュー (カスタム シリアル)、物理キュー (カスタム シリアル) の 3 つのキューがあります。Physics Queue は物理シミュレーションを実行するために使用され、Game Queue はゲーム ロジックを実行するために使用されます。したがって、すべての更新 (1/60 秒ごと) で、各キューはそれぞれの作業を行い、他のキューでブロックをスケジュールすることによって他のキューと共有します。
問題:
GCDを使用する場合: ゲーム レベルをプレイすると、つまりキューが何らかの作業を行っているときに、メモリの問題によりアプリがクラッシュする原因となるヒープ/割り当てが非常に急速に増加します。レベルを終了し、ゲーム以外のビュー、つまりキューが何も動作していない場合、メモリはゆっくりと低下し (約 2 分かかります)、安定します。添付画像の画像のピークは、ゲームレベルを終了して外に出る直前です。その後、オブジェクトの割り当てが解除されると、メモリが着実に減少します。
GCD を使用しない場合: 他の 2 つのキューを無効にしてすべてをメイン キューで実行すると、コードからすべての同時実行性を排除すると、ヒープの大幅な増加は見られず、ゲームは問題なく動作します。
すでにインターネットで調査/試行/調査済み: ブロック キャプチャとヒープにコピーされるブロックの概念については簡単に理解していますが、よくわかりません。私の理解では、ゲーム レベルを終了して非ゲーム ビューに移動すると、割り当てが解除されると予想されるすべてのオブジェクトが割り当て解除されているため、コード内にそのようなオブジェクトを見つけることができませんでした。
質問:
- GCD を使用するアプリは、多くのブロックを作成します。多くのブロックを作成することは良い習慣ですか?
- 計測器を実行すると、急速に割り当てられているが解放されていないオブジェクトが 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 で同じヒープ成長ももたらします。しかし驚くべきことに、このコードではメモリが最初のレベルに戻ることはありません。インストゥルメントの出力は次のとおりです。
このコードの何が問題になっていますか? どんなアイデアでも役に立ちます。