2

ARCでタイトなループの下でメモリ管理を処理するための最良の方法についてもっと知りたいです。特に、私が書いているアプリにはwhile、非常に長い間ループするループがあり、ARCのベストプラクティス(私が信じていること)を実装したにもかかわらず、ヒープが維持されていることに気づきました。限りなく成長しています。

私が抱えている問題を説明するために、私は最初に次のテストを設定して故意に失敗しました。

while (true) {         
    NSMutableArray *array = [NSMutableArray arrayWithObject:@"Foo"];
    [array addObject:@"bar"]; // do something with it to prevent compiler optimisations from skipping over it entirely
}

このコードを実行し、割り当てツールを使用してプロファイリングすると、メモリ使用量が際限なく増加することがわかります。ただし、これ@autoreleasepoolを次のようにラップすると、問題がすぐに解決され、メモリ使用量が低く抑えられます。

while (true) {
    @autoreleasepool {         
        NSMutableArray *array = [NSMutableArray arrayWithObject:@"Foo"];
        [array addObject:@"bar"];
    }
}

完全!これはすべて正常に機能しているようです。また、を使用して作成された自動リリースされていないインスタンスでも正常に機能します(予想どおり)[[... alloc] init]UIKit私がクラスに参加し始めるまで、すべてがうまくいきます。

たとえば、を作成してUIButton、何が起こるかを見てみましょう。

while (true) {
    @autoreleasepool {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        button.frame = CGRectZero;
    }
}

現在、メモリ使用量は無限に増加しています。事実上、@autoreleasepool効果がないように見えます。

したがって、問題はなぜメモリが正常に機能し、メモリを抑制し続けるのかということですが、ヒープに適用すると、ヒープは成長し続けますか?@autoreleasepoolNSMutableArrayUIButton

最も重要なことは、このような無限ループでクラスを使用するときにヒープが無限に拡張しないようにするにはどうすればよいですか。これにより、ARCinまたはスタイルループUIKitのベストプラクティスについて何がわかりますか。while(true)while(keepRunningForALongTime)

これについての私の直感は(そして私は完全に間違っている可能性があります)、おそらくwhile (true)ランループが循環しないようにする方法についての何かであり、UIKitインスタンスを解放するのではなくメモリに保持しています...しかし明らかに私は何かが欠けていますARCについての私の理解!

(そして明らかな原因を取り除くために、NSZombiedEnabledは有効になっていません。)

4

3 に答える 3

5

UI* オブジェクトが際限なく成長するのはなぜですか? 内部実装の詳細。ほとんどの場合、メインの実行ループをブロックすることによって効果的に無効にしている、ある種のキャッシュまたは実行ループの相互作用です。

これにより、本当の答えが得られます。

最も重要なことは、このような無限ループで UIKit クラスを使用するときにヒープが無限に拡大しないようにするにはどうすればよいかということです。これは、while(true) または while(keepRunningForALongTime) スタイルのループでの ARC のベスト プラクティスについて何を教えてくれますか?

修正方法は? メイン スレッドでタイトなループを使用しないでください。また、メインの実行ループをブロックしないでください

while(...)UI* によって引き起こされるヒープの増加を把握して回避したとしても、メイン スレッドでループを使用すると、プログラムは動作しません。iOS アプリケーション (および Cocoa アプリケーション) の全体的な設計は、メイン スレッドにメイン実行ループがあり、そのメイン実行ループは自由に実行できる必要があるということです。

そうでない場合は?アプリはユーザー入力に応答せず (最終的にはシステムによって強制終了されます)、描画コードが期待どおりに動作する可能性は低くなります (実行ループがダーティ リージョンを結合し、メイン スレッドと連携してオンデマンドで描画するため)。セカンダリ スレッドへのオフロード)。

于 2013-01-13T18:05:47.850 に答える
1

ここでの私の憶測ですが、UI 関連のオブジェクトは特に GCD など (例えば performSelectorOnMainThread:…) を使用して、何らかのアクションがメイン スレッドで確実に行われるようにする傾向があるという事実に要約できます。これはご想像のとおりです。キューに入れられたブロックまたはその他の実行単位は、インスタンスへの参照を保持し、実行ループ内で実行されるまで待機し、それを取得することはありません。

原則として、実行ループをブロックするのは良くありません。むかしむかし、それは比較的一般的でした-ドラッグ追跡はしばしばこの方法で行われました(または、ドラッグの進行中に特別なモードでのみランループを実行することにより、効果的に行われました)。しかし、多くのコードは可能性を念頭に置いて設計されていないため、特に非同期性が王様である GCD の世界では、奇妙な相互作用やデッドロックさえも引き起こします。

必要に応じて、while ループ内で runloop を明示的に実行できることを覚えておいてください。これは、自然に実行させるのとまったく同じではありませんが、通常は機能します。

于 2013-01-13T17:00:54.383 に答える
0

そうです、ランループのブロックに関するbbumWade Tegaskisからの多大な貢献とともに、これについてもう少し考えてみperformSelector:withObject:afterDelay:ました。 runloop は続行しますが、ループを将来的に継続するようにスケジュールします。

たとえば、 を使用した元の例に戻るにはUIButton、これを次のようなメソッドとして書き直す必要があります。

- (void)spawnButton {
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    button.frame = CGRectZero;
    [self performSelector:@selector(spawnButton) withObject:nil afterDelay:0];
}

このように、メソッドはすぐに終了buttonし、スコープ外に出ると正しく解放されますが、最後の行では、0 秒後に (つまり、できるだけ早く) 実行ループを再度spawnButton実行するように指示し、実行ループを実行するように指示します。 spawnButton..などなど、あなたはアイデアを得る。

あとは、コード内の別の場所を呼び出し[self spawnButton]てサイクルを開始するだけです。

これは、基本的に同じことを行う次のコードを使用して、GCD を使用して同様に解決することもできます。

- (void)spawnButton {
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    button.frame = CGRectZero;
    dispatch_async(dispatch_get_main_queue(), ^{
        [self spawnButton];
    });
}

ここでの唯一の違いは、メソッド呼び出しが GCD を使用してメイン キュー (メイン ランループ) に (非同期で) ディスパッチされることです。

Instruments でプロファイリングすると、割り当てメモリ全体が増加しているにもかかわらず、ライブ メモリは低く静的なままであることがわかります。これは、実行ループが循環し、古いUIButtons が割り当て解除されていることを示しています。

このようなランループについて考え、performSelector:withObject:afterDelay:または GCD を使用することで、この種のアプローチがランループのロックアップによって引き起こされる意図しない「メモリ リーク」を防ぐことができる (UIKit だけでなく) 他の多くのインスタンスが実際に存在します (私はそれを引用で使用します。この場合、私はUIKitのローグでした..しかし、このテクニックが役立つ他のケースがあります.)

于 2013-01-30T23:02:45.213 に答える