10

シャットダウン時に自動解放プールがクラッシュするという問題があり、ウィンドウを作成して閉じるだけの以下の小さなテスト ケースに縮小しました。-fobjc-arcフラグが取り除かれると、クラッシュは消えます。OS X 10.8.2、Clang 4.1 (421.11.66) で実行されます。ARC をより深く理解している人が、ここで何が起こっているのかを教えてくれることを願っています。 ARCはこれをすべて処理するつもりだったのですか?

スタック トレースは次のとおりです。

0   libobjc.A.dylib                 0x00007fff8fad4f5e objc_release + 14
1   libobjc.A.dylib                 0x00007fff8fad4230 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 464
2   com.apple.CoreFoundation        0x00007fff99d22342 _CFAutoreleasePoolPop + 34
3   com.apple.Foundation            0x00007fff936e84fa -[NSAutoreleasePool drain] + 154
4   com.apple.Foundation            0x00007fff936effa0 _NSAppleEventManagerGenericHandler + 125
5   com.apple.AE                    0x00007fff93a5ab48 aeDispatchAppleEvent(AEDesc const*, AEDesc*, unsigned int, unsigned char*) + 307
6   com.apple.AE                    0x00007fff93a5a9a9 dispatchEventAndSendReply(AEDesc const*, AEDesc*) + 37
7   com.apple.AE                    0x00007fff93a5a869 aeProcessAppleEvent + 318
8   com.apple.HIToolbox             0x00007fff8d0c18e9 AEProcessAppleEvent + 100
9   com.apple.AppKit                0x00007fff8e95c916 _DPSNextEvent + 1456
10  com.apple.AppKit                0x00007fff8e95bed2 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 128
11  com.apple.AppKit                0x00007fff8e953283 -[NSApplication run] + 517
12  Test                            0x00000001070e1d68 main + 152 (Test.mm:31)
13  libdyld.dylib                   0x00007fff8e10c7e1 start + 1

テスト ケースのコードは次のとおりです。

// Tested with `clang++ -fobjc-arc -g Test.mm -framework Cocoa -o Test && ./Test`

#import <Cocoa/Cocoa.h>

@interface MyApplication : NSApplication
@end
@implementation MyApplication
- (void) applicationDidFinishLaunching: (NSNotification *) note
{
    NSWindow * window = [[NSWindow alloc] initWithContentRect: NSMakeRect(100, 100, 100, 100)
                        styleMask: NSTitledWindowMask backing: NSBackingStoreBuffered defer: YES];

    [window close];

    [super stop: self];
}
@end

int main()
{
    @autoreleasepool
    {
        const ProcessSerialNumber psn = { 0, kCurrentProcess };
        TransformProcessType(&psn, kProcessTransformToForegroundApplication);
        SetFrontProcess(&psn);

        [MyApplication sharedApplication];
        [NSApp setDelegate: NSApp];

        [NSApp run];
    }

    return 0;
}
4

3 に答える 3

12

Instruments の Zombies プロファイルを使用すると、NSWindow オブジェクトが への呼び出しによって自動解放プールに入れられることがわかりましたclose:applicationDidFinishLaunching:ARC は、NSWindow インスタンスを完了して破棄すると、正しく参照カウントがゼロになります。ただし、自動解放プールは、現在機能していない NSWindow インスタンスを引き続き認識しており、シャットダウン時にそれを解放しようとするため、クラッシュが発生します。

ARC の下で管理されているオブジェクトの自動解放は、自動解放プールがそのオブジェクトへの弱い参照をゼロにすることを保持していない限り、悪い考えのように思えます。

を追加して、閉じるときにウィンドウを自動解放しないように指示することで、この問題を防ぐことができます[window setReleasedWhenClosed: NO];

于 2012-11-20T10:00:00.237 に答える
3

ARC は、現在のスコープよりも大きな範囲を持つ変数に割り当てた場合にのみ、新しく作成されたオブジェクトを保持します。そうしないと、オブジェクトがリークされます。

あなたの例では、NSWindowalloc を呼び出して の新しいインスタンスを作成しています。これにより、一時的に所有権がローカル変数 に転送されますwindow。その変数はメソッドの最後で存在しなくなるため、ARC はreleaseウィンドウ インスタンスのリークを避けるために呼び出しを挿入する必要があります。その結果、インスタンスはもはや何にも所有されていないため、自身の割り当てを解除します。

これを修正するには、セマンティクスNSWindowを持つ型のプロパティを宣言しstrong、ウィンドウ インスタンスをプロパティ セッター メソッドに渡します (または、対応するインスタンス変数に直接代入します。どちらでも機能します)。

編集

明確にするために、宣言されたプロパティ(または少なくともインスタンス変数)をに追加する必要があります。MyApplicationたとえば、

@interface MyApplication : NSApplication

@property (strong, nonatomic) NSWindow *window;

@end

次に、 の実装でapplicationDidFinishLaunching、次のプロパティを設定します。

@implementation MyApplication

- (void) applicationDidFinishLaunching: (NSNotification *) note
{
    NSWindow *window = [[NSWindow alloc] initWithContentRect:NSMakeRect(100, 100, 100, 100)
                        styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:YES];

    self.window = window;

    ...
}

@end
于 2012-11-13T16:42:52.640 に答える
0

ARC では、より長く浮いていると思われるものにプロパティを使用する必要があります。

プライベート プロパティを抽象化するには、.m ファイルで匿名カテゴリを使用します。

http://developer.apple.com/library/mac/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.htmlに詳細があります。

これで修正されます:

@interface MyApplication : NSApplication
@property NSWindow *window;

@end

@implementation MyApplication
- (void) applicationDidFinishLaunching: (NSNotification *) note
{
  self.window = [[NSWindow alloc] initWithContentRect: NSMakeRect(100, 100, 100, 100)
                                                  styleMask: NSTitledWindowMask backing: NSBackingStoreBuffered defer: YES];

  [self.window close];

  [super stop: self];
}
@end

int main()
{
  @autoreleasepool
  {
    const ProcessSerialNumber psn = { 0, kCurrentProcess };
    TransformProcessType(&psn, kProcessTransformToForegroundApplication);
    SetFrontProcess(&psn);

    [MyApplication sharedApplication];
    [NSApp setDelegate: NSApp];

    [NSApp run];
  }

  return 0;
}

それが役立つことを願っています。

于 2012-11-20T09:34:03.417 に答える