2

コンテキスト: GCD を使用する iOS ゲーム アプリケーションがあります。アプリケーションには、メイン キュー、ゲーム ロジック キュー (カスタム シリアル)、物理キュー (カスタム シリアル) の 3 つのキューがあります。Physics Queue は物理シミュレーションを実行するために使用され、Game Queue はゲーム ロジックを実行するために使用されます。したがって、すべての更新 (1/60 秒ごと) で、各キューはそれぞれの作業を行い、他のキューでブロックをスケジュールすることによって他のキューと共有します。

問題:

GCDを使用する場合: ゲーム レベルをプレイすると、つまりキューが何らかの作業を行っているときに、メモリの問題によりアプリがクラッシュする原因となるヒープ/割り当てが非常に急速に増加します。レベルを終了し、ゲーム以外のビュー、つまりキューが何も動作していない場合、メモリはゆっくりと低下し (約 2 分かかります)、安定します。添付画像の画像のピークは、ゲームレベルを終了して外に出る直前です。その後、オブジェクトの割り当てが解除されると、メモリが着実に減少します。

GCD を使用しない場合: 他の 2 つのキューを無効にしてすべてをメイン キューで実行すると、コードからすべての同時実行性を排除すると、ヒープの大幅な増加は見られず、ゲームは問題なく動作します。

すでにインターネットで調査/試行/調査済み: ブロック キャプチャとヒープにコピーされるブロックの概念については簡単に理解していますが、よくわかりません。私の理解では、ゲーム レベルを終了して非ゲーム ビューに移動すると、割り当てが解除されると予想されるすべてのオブジェクトが割り当て解除されているため、コード内にそのようなオブジェクトを見つけることができませんでした。

質問:

  1. GCD を使用するアプリは、多くのブロックを作成します。多くのブロックを作成することは良い習慣ですか?
  2. 計測器を実行すると、急速に割り当てられているが解放されていないオブジェクトが 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 で同じヒープ成長ももたらします。しかし驚くべきことに、このコードではメモリが最初のレベルに戻ることはありません。インストゥルメントの出力は次のとおりです。

ここに画像の説明を入力

このコードの何が問題になっていますか? どんなアイデアでも役に立ちます。

4

3 に答える 3

3

CADisplayLink を 60 秒ごとに起動したときに、GCD キューの周りでこれと同じ種類のメモリの蓄積が見られましたが、フレーム レンダリング ブロックが完了するのにそれよりも時間がかかりました。ブロックはキューに積み上げられ、ご覧のとおり、ブロックに関連するオーバーヘッドがあります。

Mike Ash はこれについて素晴らしい記事を書いており、処理ブロックを構築することの結果と、このプレッシャーの一部を軽減する方法を示しています。さらに、これは GCD の最近の WWDC セッションで、Instruments でこれを診断する方法とともにカバーされていましたが、現在特定のセッションを見つけることができません。

私の場合、Mike がたどり着いたものと同様のものを使用することになり、ディスパッチ セマフォを使用してメモリ内にブロックが蓄積するのを防ぎました。コードとともに、この回答でこのアプローチについて説明します。私がしているのは、最大カウントが 1 のセマフォを使用し、新しいブロックをディスパッチする前にそれを確認することです。そのタイプの別のブロックがシリアル キューにある場合、私は保釈し、別のブロックを山に投げません。ブロックの実行が終了したら、別のセマフォを追加できるようにセマフォの数を減らします。

ゲームの負荷が増加するにつれてフレームをドロップできるようにする必要があるため、キューへの新しいブロックの追加を管理するには、このようなものが必要なようです。

于 2013-04-26T15:12:28.803 に答える
0

メインキューには、実行ループの反復ごとに排出される自動解放プールがあります

同時キューは考えていません..少なくともNSThreadsはデフォルトではそうではありません

@autoreleasepool のキューにコードをラップします

于 2013-04-26T07:49:42.057 に答える