NSAutoreleasePool 自動解放プールはどのように機能しますか?
-
09-06-2019 - |
質問
私の理解では、 割り当てる, 新しい, 、 または コピー 手動で解除する必要があります。例えば:
int main(void) {
NSString *string;
string = [[NSString alloc] init];
/* use the string */
[string release];
}
しかし、私の質問は、これも同様に有効ではないでしょうか?:
int main(void) {
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
NSString *string;
string = [[[NSString alloc] init] autorelease];
/* use the string */
[pool drain];
}
解決
はい、2 番目のコード スニペットは完全に有効です。
-autorelease がオブジェクトに送信されるたびに、そのオブジェクトは最も内側の autorelease プールに追加されます。プールが空になると、プール内のすべてのオブジェクトに -release が送信されます。
自動リリース プールは、リリースの送信を「後で」まで延期できるようにするための単なる便宜です。この「後で」はいくつかの場所で発生する可能性がありますが、Cocoa GUI アプリで最も一般的なのは、現在の実行ループ サイクルの終了時です。
他のヒント
NSAutoreleasePool:ドレイン vs.リリース
の機能があるので、 drain
そして release
混乱を引き起こしているようですが、ここで明確にする価値があるかもしれません(ただし、これについてはで説明されています) 文書...).
厳密に言うと、全体像の観点から言えば、 drain
は ない に相当 release
:
参照カウント環境では、 drain
と同じ操作を実行します release
, したがって、その意味ではこの 2 つは同等です。強調すると、これはあなたがそうすることを意味します ない 使用するとプールが漏れる drain
それよりも release
.
ゴミが集まる環境では、 release
ノーオペです。したがって、効果はありません。 drain
, 一方、「必要に応じて収集する」というコレクターへのヒントが含まれています。したがって、ガベージ コレクション環境では、次を使用します。 drain
システムの収集スイープのバランスをとるのに役立ちます。
すでに指摘したように、2 番目のコード スニペットは正しいです。
すべての環境 (参照カウント、GC、ARC) で機能し、ドレイン/リリースの混乱を避ける、自動解放プールのより簡潔な使用方法を提案したいと思います。
int main(void) {
@autoreleasepool {
NSString *string;
string = [[[NSString alloc] init] autorelease];
/* use the string */
}
}
上の例では、次の点に注意してください。 @autoreleasepool ブロック。これは文書化されています ここ.
いいえ、違います。ドキュメントには、非 GC では -drain が -release と同等であることが明確に記載されており、これは NSAutoreleasePool が ない 漏洩される。
Apple から読んだ内容:「自動解放プール ブロックの最後で、ブロック内で自動解放メッセージを受信したオブジェクトに解放メッセージが送信されます。オブジェクトは、ブロック内で自動解放メッセージが送信されるたびに解放メッセージを受け取ります。」
release の代わりに autorelease をオブジェクトに送信すると、少なくともプール自体が空になるまで、そのオブジェクトの存続期間が延長されます (オブジェクトがその後保持される場合はさらに長くなる可能性があります)。オブジェクトは同じプールに複数回入れることができ、その場合、オブジェクトはプールに入れられるたびに解放メッセージを受け取ります。
はいといいえ。これをガベージ コレクション (メモリ管理ではない) 環境で実行した場合、文字列メモリは解放されますが、リリースの代わりにドレインを使用することで NSAutoreleasePool オブジェクトがメモリに「リーク」されてしまいます。この「リーク」は、単に NSAutoreleasePool のインスタンスを GC の下で強力なポインタを持たない他のオブジェクトと同様に「到達不能」にするだけであり、オブジェクトは次回 GC が実行されるときにクリーンアップされます。これはおそらく、次の GC の呼び出しの直後である可能性があります。 -drain
:
ドレイン
ガベージ コレクション環境では、最後のコレクション以降に割り当てられたメモリが現在のしきい値を超えている場合にガベージ コレクションをトリガーします。それ以外の場合はリリースとして動作します。...ガベージ コレクション環境では、このメソッドは最終的に
objc_collect_if_needed
.
それ以外の場合は、方法と同様です -release
はい、非 GC の下で動作します。他の人も述べているように、 -release
GC では何も行われないため、プールが GC で適切に機能することを確認する唯一の方法は、 -drain
, 、 そして -drain
非 GC ではまったく同じように動作します -release
非 GC の下で動作し、おそらくその機能もより明確に伝えられます。
「new、alloc、または init で呼び出されたもの」というステートメントには、「init」を含めるべきではありません (ただし、「copy」は含める必要があります)。「init」はメモリを割り当てず、オブジェクト (コンストラクター) をセットアップするだけであるため、注意が必要です。ファッション)。alloc されたオブジェクトを受け取り、関数がそのように init だけを呼び出した場合、それを解放しません。
- (void)func:(NSObject*)allocd_but_not_init
{
[allocd_but_not_init init];
}
これにより、既に開始したメモリよりも多くのメモリが消費されることはありません (init がオブジェクトをインスタンス化しないと仮定しますが、とにかくそれらについては責任を負いません)。