6

前文; これは、「リークのある巨大なアプリを持っています」という一般的な質問ではありません。これは、完全なソース コードを使用したほぼ自明なデモ アプリで自動参照カウントが適切に機能しないか、微妙なコード生成またはコンパイラの問題、または Instruments のバグのいずれかに関する特定の問題です。(TLDR: ああ。実際には奇妙な小さな競合状態です)

Instrumentsの「割り当て」リストにインスタンスリークが表示されているという事実に混乱していますが、そのクラスのインスタンスは1つしかなく、ARCがdeallocメソッドを呼び出しており、それが呼び出されていることがわかっています。解放が完了すると出力される NSLog メッセージがありますが、Instruments のリークのリストにはまだ表示されます。

保持カウントが 1 を超えることはありません。誰にも保持されておらず、dealloc されていますが、Instrument のリークでアクティブなインスタンスとして表示されるため、「リーク」のように見えます。

そんなことがあるものか?

私はまだ ARC で Objective-C を学んでいるので、よくある初心者の間違いを犯しているに違いないと思います。これが私の唯一のinitと私のオブジェクトのdeallocです:

- (id) initWithMessage:(NSString*)messageForUser
{
    self = [super init];
    if (self)
    {
        _message = messageForUser;
        NSLog( @"from constructor: %@",_message);
    }
    return self;

}

- (void)dealloc {
    NSLog(@"Goodbye cruel world. One WPMyObject signing off.");
   // [message release]; // ARC forbiddeth thee! Begone release.
    _message = nil;

   // [super dealloc]; // ARC forbiddeth explicit super dealloc
}

できるかどうかを確認するため[super dealloc]に、dealloc メソッドを呼び出そうとしましたが、ARC はエラーでユーザーをブロックしました。ただし、独自の init メソッドを作成すると、ブロックされません。

XCodeで「実行」を使用してプログラムを実行すると、「さようなら残酷な世界」NSLogメッセージが表示されます.メモリ リーク テンプレートを使用した Instruments 分析が使用されている場合は、次のように実行します。

以下のインスタンス作成コードなしで実行すると、標準ライブラリの malloc リークがいくつか報告されるだけですが、このコードを追加すると、すべてのリークが発生します。

  WPMyObject * myObject = [[WPMyObject alloc] initWithMessage: @"Hello World!\n" ];

これが私が見ているものであり、WPMyObjectの唯一のインスタンスがリークしていることを理解していると思います:

ここに画像の説明を入力

非常に簡単な完全なソース コード (Objective-C と を使用した小さな Mac OS X コマンド ライン アプリFoundation/Foundation.h) は BitBucket にあります。このリンクをクリックして、ブラウザでソース コードを表示します。

main.m ユニットは、@autoreleasepool {...}コンテキスト ステートメント内で単一のテスト オブジェクト インスタンスの作成を実行します。

ここに画像の説明を入力

自分のコンピューターでまったく簡単なコードを見たい場合は、次のように取得します。

  hg clone https://bitbucket.org/wpostma/objectivecplaymac

}更新:自動解放プールを終了する の直前に次のコード行を追加することで、「リーク」(Instruments のバグ、または clang/llvm コンパイラのバグであり、「実際のリーク」ではない可能性があります) を修正できます。

  NSLog( @"Reached end of autorelease pool" );

うん。ログメッセージを追加。「漏れ」が消えます。これは Mac OS X 10.7.5 上の XCode 4.5.2 (4G2008a) で、Instruments バージョン 4.5 (ビルド 4523) が含まれています。

Update2: これは単純なマルチプロセスの競合状態です。以下のコードは、妥当なデバッグ ビルドのハッカーのようです。「Instruments が処理を完了するまで待ってから、main() を終了する」というより明確な方法があれば、Apple エンジニアの将来の優れた機能になる可能性があります。PROFILER_SYNC("MESSAGE") マクロは、リリース モードでは何も展開されませんが、デバッグ ビルドでは "MESSAGE" をプロファイラーに送信します。これは非常に便利です。

int main(int argc, const char * argv[])
{
    // This creates and cleans up an auto-release pool context.
    @autoreleasepool {

        foo(); // Microscopic amounts of debug code you want profiler to analyze go here.

#ifdef DEBUG
        usleep(10000); // race condition prevention in debug builds, so Instruments can finish up.
#endif

    }
#ifdef DEBUG
    usleep(10000); // race condition prevention in debug builds, so Instruments can finish up.
#endif

   return 0;
}
4

1 に答える 1

3

リークというよりは競合状態のように聞こえます。NSLogの書き込みとプログラムの実行が終了するまでの競合。または、特定のしきい値に達するまで出力がフラッシュされない可能性が高い、バッファリングの問題です。

sleep(100);自動解放プールの右中括弧の後に を付けてみてください。または、 のログ メッセージにさらに多くのテキストを追加してみてくださいdealloc

それでも「修正」されない場合は、逆アセンブリを表示して、2 つのバージョンのコード間で何が変更されているかを確認します。


これが発生する理由: Instruments と NSLog() の両方が、パフォーマンス上の理由から効果的にバッファリングされます。これらは、ほとんどの場合、メイン イベント ループまたは への呼び出しのいずれかを含む、比較的長時間実行されるプロセスで使用するように設計されていますdispatch_main()

これは、ターゲット アプリケーションのパフォーマンスへの影響を最小限に抑えるために行われますが、プロセスの寿命が非常に短いマイクロ ベンチマークでは奇妙な結果になる可能性があります。

短期間のプロセスで最小限のテスト ケースがある場合は、 で閉じることをお勧めmain()[[NSRunLoop currentLoop] run];ます。それは永久に実行され、Instruments および/またはデバッガーのために実行可能なランタイムを提供します。

于 2013-01-11T17:13:53.993 に答える