10

私は何かを明確にしたかった。

次のコードがあるとしましょう:

- (void) viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  for (int i = 0; i < 5000000; i++) {
    NSString *s = [NSString stringWithFormat:@"Hello, %@!", @"World"];
  }
}

これにより、この関数呼び出し内で 500 万の自動解放された文字列が作成されます。私が見る唯一の@autoreleasepoolはmain.mでアプリケーションのインスタンス化をラップするものであるため、これがアプリケーションの終了までこれらのオブジェクトを保持することを期待していました。しかし、そうではありません。この関数呼び出しの最後に、それらはすべてリリースが呼び出され、メモリから削除されたようです。

このドキュメント:

https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html

「アプリケーション キットは、イベント ループの各サイクルの開始時にメイン スレッドに自動解放プールを作成し、最後にそれを排出することで、イベントの処理中に生成された自動解放されたオブジェクトを解放します」と述べています。

それは私には理にかなっていますが、これは UIKit の下にあり、Application Kit ではありません。私の質問は、この場合、UIKit/Cocoa Touch は同じことを行うのでしょうか、それともオブジェクトが解放される別の説明がありますか?

ありがとう!

4

3 に答える 3

20

アンドリューは、メインの実行ループのサイクルごとに自動解放プールが排出されるという主な質問に答えました。viewDidLoadそのため、メインの実行ループに戻ると、作成された autorelease オブジェクトはすぐに排出される可能性があります。それらは確かに「アプリケーションが終了するまで」保持されません。

ただし、注意が必要です。これらのオブジェクトが自動解放プールに追加されていると想定していることは明らかです。この仮定にはいくつかの注意事項があります。

  1. alloc以前は (現在も ARC-MRC の相互運用性のために必要)、名前が、newcopy、またはで始まらないメソッドからオブジェクトを返す場合mutableCopy、それらのオブジェクトはオブジェクトを自動解放し、自動解放プールが空になったとき (つまり、実行ループに戻ります)。

  2. しかし、ARC は、自動解放プールの必要性を最小限に抑えることについてより賢くなりました ( http://rentzsch.tumblr.com/post/75082194868/arcs-fast-autoreleasecallerAcceptsFastAutoreleaseを参照してください。これは、現在callerAcceptsOptimizedReturn呼び出されている について説明しprepareOptimizedReturnています)。autorelease行動。そのため、ライブラリと呼び出し元の両方が ARC を使用している場合、オブジェクトは自動解放プールに配置されない可能性がありますが、不要な場合は ARC が巧妙にそれらをすぐに解放します。

    現在の ARC プロジェクトでは、通常、自動リリース プールは必要ありません。ただし、特定の特殊なケースでは、自動解放プールを使用することでメリットが得られます。以下に、そのケースの 1 つを概説します。

次のコードを検討してください。

#import "ViewController.h"
#import <sys/kdebug_signpost.h>

typedef enum : NSUInteger {
    InnerLoop = 1,
    MainLoop = 2
} MySignPostCodes;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 500; i++) {
            NSData *data = [NSData dataWithContentsOfURL:fileURL];
            UIImage *image = [[UIImage alloc] initWithData:data];
            NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
            [NSThread sleepForTimeInterval:0.01];
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

次のコードは、自動解放プールに 500,000 個のオブジェクトを追加します。これは、実行ループに戻ったときにのみ排出されます。

プールなし

この場合、自動解放プールを使用して最高水準点を最小限に抑えることができます。

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 5; j++) {
            @autoreleasepool {
                kdebug_signpost_start(InnerLoop, 0, 0, 0, 2);
                for (long i = 0; i < 100; i++) {
                    NSData *data = [NSData dataWithContentsOfURL:fileURL];
                    UIImage *image = [[UIImage alloc] initWithData:data];
                    NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
                    [NSThread sleepForTimeInterval:0.01];
                }
                kdebug_signpost_end(InnerLoop, 0, 0, 0, 2);
            }
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

プール

要するに、ARC では、いつ autorelease オブジェクトを使用したか、および変数がスコープ外になったときにそれを明示的に解放したかが常に明らかであるとは限りません。これは、Instruments の動作を調べることでいつでも確認できます。

余談ですが、このNSStringクラスは高度に最適化されており、標準のメモリ管理慣行に常に準拠しているわけではないため、このクラスを使用する際に一般的なメモリ管理の結論を出しすぎることには注意が必要です。

于 2013-11-07T17:04:37.820 に答える
7

はい、UIKit は同じことを行います。システムによって作成されたメイン スレッドの自動解放プールは、すべての実行ループ サイクルの終了時に排出されます。独自のコードでは、この正確な有効期間に依存しないことをお勧めします。(NSThread などを使用して) 手動で新しいスレッドを作成する場合は、そのスレッドで自動解放プールを作成する必要があります。

編集:Rob の回答は、ARC での動作に関するいくつかの優れた追加情報を提供します。一般に、ARC が実行できるいくつかの最適化により、オブジェクトが自動解放プールに入る可能性は低くなると言えます。

于 2013-11-07T16:24:23.193 に答える
0

オブジェクトを保持していた参照に新しいオブジェクトを割り当てると、元のオブジェクトがすぐに解放されると仮定します(他に何も指していない場合-参照カウントはゼロになります)ARCを使用strongし、ループのようにデフォルトの参照を想定します例。

MyObject *object = [[MyObject alloc] init]; // obj1, ref count 1 because strong
object = [[MyObject alloc] init]; // obj2, ref count of obj1 should be 0
                                  // so obj1 gets released

ARC リリース ノートへの移行のアップル ノート

保持/解放呼び出しが配置される場所について考えるのをやめて、代わりにアプリケーション アルゴリズムについて考えてみてください。オブジェクト内の「強いポインターと弱い」ポインター、オブジェクトの所有権、保持サイクルの可能性について考えてください。

clang Clang 3.4 documentation OBJECTIVE-C AUTOMATIC REFERENCE COUNTING (ARC)releaseから、オブジェクトに新しい値が割り当てられたときに が呼び出されるようです。

代入演算子を評価するときに代入が発生します。セマンティクスは修飾によって異なります。

__strong オブジェクトの場合、新しい指示先が最初に保持されます。次に、左辺値にプリミティブ セマンティクスが読み込まれます。3 番目に、新しい pointee がプリミティブ セマンティクスを使用して左辺値に格納されます。そして最後に、古い指先が解放されます。これはアトミックには実行されません。外部同期を使用して、同時ロードおよびストアに直面してもこれを安全にする必要があります。

于 2013-11-08T02:54:36.753 に答える