3

特定のクラスのインスタンスをキャッシュしたい。クラスはすべてのインスタンスの辞書を保持し、誰かが新しいインスタンスを要求すると、クラスは最初にキャッシュから要求を満たそうとします。ただし、メモリ管理には小さな問題があります。ディクショナリ キャッシュは挿入されたオブジェクトを保持するため、割り当てが解除されることはありません。メソッドをオーバーロードする必要がreleaseあり、保持カウントが 1 になったら、キャッシュからインスタンスを削除して、割り当てを解除できるようにする必要があります。

これは機能しますが、メソッドをいじくり回すのは苦手でrelease、解決策が非常に複雑であることがわかりました。格納するオブジェクトを保持しないハッシュ クラスを使用できると考えました。そのようなものはありますか?特定のインスタンスの最後のユーザーがそれを解放すると、そのインスタンスはキャッシュから自動的に消えるという考え方です。

NSHashTableは私が探しているもののようですが、ドキュメントには「ガベージ コレクション環境での弱い関係のサポート」について記載されています。ガベージコレクションなしでも機能しますか?


明確化:誰かが本当にインスタンスを必要としない限り、インスタンスをメモリに保持する余裕はありません。そのため、最後の「実際の」ユーザーがインスタンスを解放したときにインスタンスをキャッシュから消去したいと考えています。


より良い解決策:これは iPhone でした。いくつかのテクスチャをキャッシュしたかったのですが、一方で、最後の実際の所有者がそれらを解放したらすぐにそれらをメモリから解放したかったのです。これをコーディングする簡単な方法は、別のクラスを使用することです (これを と呼びましょうTextureManager)。このクラスはテクスチャ インスタンスを管理し、それらをキャッシュします。これにより、同じ名前のテクスチャに対する後続の呼び出しがキャッシュから提供されます。最後のユーザーがテクスチャを解放したときに、すぐにキャッシュを消去する必要はありません。テクスチャをメモリにキャッシュしておくだけで、デバイスのメモリが不足すると、メモリ不足の警告が表示され、キャッシュを削除できます。Textureこれはより良い解決策です。キャッシングがクラスを汚さず、いじる必要がなくrelease、キャッシュ ヒットの可能性がさらに高くなるためです。のTextureManagerに抽象化できるResourceManagerため、テクスチャだけでなく他のデータもキャッシュできます。

4

6 に答える 6

5

必要なのはゼロ化の弱参照です (これは「キャッシュ管理アルゴリズムのグラール」ではなく、よく知られているパターンです)。問題は、手動メモリ管理プログラムではなく、ガベージ コレクションで実行している場合にのみ、Objective C が弱い参照をゼロにすることを提供することです。また、iPhone は (まだ) ガベージ コレクションを提供していません。

これまでのすべての答えは、半分の解決策を示しているようです。

非保持参照を使用するだけでは十分ではありません。これは、参照されるオブジェクトの割り当てが解除されたときに参照をゼロにする (または辞書からエントリを削除する) 必要があるためです。ただし、これは、そのオブジェクトの -dealloc メソッドが呼び出される前に実行する必要があります。そうしないと、キャッシュの存在そのものが、オブジェクトが復活するリスクにさらされます。これを行う方法は、弱参照を作成するときにオブジェクトを動的にサブクラス化し、動的に作成されたサブクラスで -release をオーバーライドしてロックを使用し、-dealloc をオーバーライドして弱参照をゼロにすることです。

これは一般的に機能しますが、無料でブリッジされた Core Foundation オブジェクトの場合は惨めに失敗します。残念ながら、この手法を無料のブリッジ オブジェクトに拡張する必要がある場合の唯一の解決策は、いくつかのハッキングと文書化されていないもの (コードと説明についてはこちらを参照) が必要なため、iOS や、オンラインで販売したいプログラムには使用できません。 Mac App ストア。

Apple ストアで販売する必要があり、文書化されていないものを避ける必要がある場合、最良の代替手段は、保持キャッシュへのロックされたアクセスを実装し、メモリを解放するときに現在の -retainCount 値が 1 の参照を探すことです。キャッシュへのすべてのアクセスがロックが保持された状態で行われる限り、ロックを保持している間にカウントが 1 になった場合、オブジェクトをキャッシュから削除した場合 (したがって解放した場合)、オブジェクトを復活させることができる人は誰もいないことがわかります。 ) ロックを放棄する前に。

iOS の場合、UIApplicationDidReceiveMemoryWarningNotification を使用して清掃をトリガーできます。Mac では、独自のロジックを実装する必要があります。おそらく、定期的なチェックだけ、または定期的な清掃だけです (どちらのソリューションも iOS でも機能します)。

于 2011-01-13T16:49:24.180 に答える
5

はい、NSHashTable を使用して、本質的に非保持辞書を作成できます。または、リリースと保持のコールバックのために NULL を指定して CFDictionaryCreate を呼び出すこともできます。次に、無料のブリッジングのおかげで結果を単純に NSDictionary に型キャストし、保持カウントをいじらないことを除いて、通常の NSDictionary と同じように使用できます。

これを行うと、ディクショナリは参照を自動的にゼロにしないため、インスタンスの割り当てを解除するときに必ず参照を削除する必要があります。

于 2009-01-06T09:36:27.640 に答える
2

NSMutableDictionary を使用し、UIApplicationDidReceiveMemoryWarningNotification に登録することで、この種のことを実装しました。メモリ警告で、retainCountが1の辞書から何かを削除します...

于 2010-05-11T19:00:02.720 に答える
1

[NSValue valueWithNonretainedObject:]を使用して、インスタンスをNSValueでラップし、それをディクショナリに配置します。インスタンスdeallocメソッドで、対応するエントリをディクショナリから削除します。保持をいじることはありません。

于 2010-05-11T20:05:38.737 に答える
0

これにアプローチする方法は、キャッシュ内のオブジェクトが使用されているかどうかを示す別のカウントまたはフラグをどこかに維持することだと思います。オブジェクトの処理が完了したときにこれをチェックするか、n 秒ごとにチェックを実行して、オブジェクトを解放する必要があるかどうかを確認できます。

オブジェクトをディクショナリから削除する前にオブジェクトを解放することを含む解決策は避けます (NSValue を使用することは、valueWithNonretainedObject:これを達成するための別の方法です)。長期的には問題を引き起こすだけです。

于 2009-01-06T14:36:57.707 に答える
0

私の理解では、キャッシュ管理アルゴリズムの Graal を実装したいと考えています。つまり、使用されなくなったアイテムをドロップします。

最も最近要求されていないアイテムを削除するなど、他の基準を検討することもできます。

于 2009-01-06T09:57:37.643 に答える