objective-c swift - ARCで@autoreleasepoolが必要なのはなぜですか?




3 Answers

@autoreleasepoolは何も自動@autoreleasepoolしません。 ブロックの終わりに達すると、ブロックがアクティブである間にARCによって自動リリースされたオブジェクトにリリースメッセージが送信されるように、自動解放プールが作成されます。 アップルの「 アドバンストメモリ管理プログラミングガイド」では、このように説明しています。

自動解放プールブロックの最後に、ブロック内で自動解放メッセージを受信したオブジェクトに解放メッセージが送信されます。ブロック内の自動解放メッセージが送信されるたびに解放メッセージが受信されます。

objective-c memory-management xcode4.2 automatic-ref-counting

ARC(Automatic Reference Counting)の大部分については、Objective-Cオブジェクトを使用してメモリ管理について考える必要はありません。 もうNSAutoreleasePoolを作成することはできませんが、新しい構文があります:

@autoreleasepool {
    …
}

私の質問は、私が手動でリリース/オートリリースする予定でないときに、なぜこれが必要なのでしょうか?

編集:私はすべてのanwersとコメントから抜け出したものを要約するために:

新しい構文:

@autoreleasepool { … }は新しい構文です

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];

さらに重要なことには:

  • ARCはautorelease releaseます。
  • これを行うには、自動リリースプールが必要です。
  • ARCは自動解放プールを作成しません。 しかしながら:
    • すべてのCocoaアプリのメインスレッドには既に自動解放プールがあります。
  • あなたが@autoreleasepoolを利用したいと思うかもしれない2つの機会があります:
    1. セカンダリスレッドで、自動解放プールがない場合、 myRunLoop(…) { @autoreleasepool { … } return success; }などのリークを防ぐために独自に作成する必要がありますmyRunLoop(…) { @autoreleasepool { … } return success; } myRunLoop(…) { @autoreleasepool { … } return success; }
    2. @mattjgallowayが答えとして示したように、より多くのローカルプールを作成したいとき。



これは、オートレリースされたオブジェクトがスコープから外れるのが安全かどうかのヒントをコンパイラに提供する必要があるからです。




メソッドから新しく作成されたオブジェクトを返すには、オートリアーゼプールが必要です。 たとえば、次のコードを考えてみましょう。

- (NSString *)messageOfTheDay {
    return [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
}

メソッドで作成された文字列の保持カウントは1になります。 その人とリリースのバランスをとるのは誰でしょうか?

メソッド自体は? 不可能なので、作成されたオブジェクトを返さなければならないので、返す前に解放してはいけません。

メソッドの呼び出し側は? 呼び出し側は解放を必要とするオブジェクトを取得することを期待していません。メソッド名は新しいオブジェクトが作成されたことを意味するものではなく、オブジェクトが返され、返されたオブジェクトはリリースを必要とする新しいオブジェクトですよくない既存のものにしてください。 メソッドが返すものは、内部状態に依存する場合もあります。したがって、呼び出し元はオブジェクトを解放する必要があるかどうかを知ることができず、気にする必要はありません。

呼び出し元が常にすべての返されたオブジェクトを慣例によって解放しなければならない場合、新たに作成されなかったオブジェクトはすべてメソッドから返される前に保持されなければならず、一度スコープ外に出ると、それは再び返されます。 多くの場合、呼び出し元が返されたオブジェクトを常に解放しない場合、多くの場合保持カウントを変更することを完全に避けることができるので、多くの場合非常に非効率的です。

そのため、自動解放プールがあるので、最初の方法は実際には

- (NSString *)messageOfTheDay {
    NSString * res = [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
    return [res autorelease];
}

オブジェクト上でautoreleaseを呼び出すと、autoreleaseプールにそのオブジェクトが追加されますが、それは実際には自動解放プールにオブジェクトを追加するという意味ですか? まあ、それはあなたのシステムに「 私はあなたが私のためにそのオブジェクトをリリースしてもらいたいが、後で、今ではない、リリースによって均衡を保つ必要がある保持カウントを持っている。そうでなければメモリが漏れるが、今現在のスコープを越えて生き続けるためにオブジェクトが必要なので、私の呼び出し元は私のためにそれをやってくれませんが、これを行う必要はないということは知られていません。プール、また私のために私のオブジェクトをきれいにする。

ARCでは、オブジェクトを保持するタイミング、オブジェクトを解放するタイミング、オートリリースプールに追加するタイミングを決定しますが、メモリーから漏れなくメソッドから新しく作成されたオブジェクトを返すことができるようにする必要があります。 アップルは、生成されたコードに対していくつかの気のきいた最適化を行っています。実行時に自動解放プールを排除することがあります。 これらの最適化では、呼び出し元と呼び出し先の両方がARCを使用している必要があります(ARCと非ARCの混合が正当で正式にサポートされていることを忘れないでください)。

このARCコードを検討してください:

// Callee
- (SomeObject *)getSomeObject {
    return [[SomeObject alloc] init];
}

// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];

システムが生成するコードは、次のコードのように動作します(ARCコードと非ARCコードを自由に混在させる安全なバージョン)。

// Callee
- (SomeObject *)getSomeObject {
    return [[[SomeObject alloc] init] autorelease];
}

// Caller
SomeObject * obj = [[self getSomeObject] retain];
[obj doStuff];
[obj release];

(呼び出し元の保持/解放は防御的な安全保持であることに注意してください。厳密には必須ではなく、コードは完全に正しいでしょう)

また、実行時に両方ともARCを使用するよう検出された場合は、このコードのように動作します。

// Callee
- (SomeObject *)getSomeObject {
    return [[SomeObject alloc] init];
}

// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
[obj release];

ご覧のように、Appleはatuoreleaseを廃止し、プールが破棄されたときにオブジェクトのリリースが遅れ、安全が保持されるようにします。 その可能性と実際に何が起こっているのかを知るには、 このブログ記事をご覧ください。

今実際の質問に: @autoreleasepool使うのはなぜですか?

ほとんどの開発者にとって、コードでこの構造体を使用する理由は1つしかありません。それは、適切な場合にはメモリのフットプリントを小さくすることです。 例:

for (int i = 0; i < 1000000; i++) {
    // ... code ...
    TempObject * to = [TempObject tempObjectForData:...];
    // ... do something with to ...
}

tempObjectForData呼び出すたびにautoreleaseが返される新しいTempObjectが作成されることがあるとします。 forループは、現在の自動回収プールですべて収集される100万のこれらの一時オブジェクトを作成し、そのプールが破棄されると、すべての一時オブジェクトも破棄されます。 それが起こるまで、あなたは100万の一時オブジェクトを記憶しています。

代わりに次のようなコードを書くと:

for (int i = 0; i < 1000000; i++) @autoreleasepool {
    // ... code ...
    TempObject * to = [TempObject tempObjectForData:...];
    // ... do something with to ...
}

その後、forループが実行されるたびに新しいプールが作成され、各ループ反復の終わりに破棄されます。 その方法では、ループが100万回実行されているにもかかわらず、常に1つの一時オブジェクトがいつでもメモリにぶら下がっています。

過去には、メインスレッドだけが自動的にCocoa / UIKitアプリケーション用の自動解放プールを持っているため、スレッドを管理するときに(たとえばNSThreadを使用して)スレッドを管理するときに、自分自身で自動NSThreadプールを管理する必要がありました。 しかし、これはかなり今日のレガシーです。今日は、おそらくスレッドを使用しないで始めるでしょう。 あなたはGCD DispatchQueueNSOperationQueue使いますが、これらの2つはどちらもトップレベルの自動解放プールを管理し、ブロック/タスクを実行する前に作成し、一度終了すると破棄します。




Related