3

以下の両方のメソッドは、NSString を割り当ててリークします。XCode (4.6) Analyzer を実行すると、bar2 でリークが正常にフラグ付けされますが、bar1 ではそれについて言及されません。

理由がわかりません。

私の実際のプロジェクトでは、bar2 のような明らかな方法で捕捉されると予想されるリークを発見しましたが、bar1 での同じ動作のために発見されませんでした。

理由を理解するのを手伝ってください。ありがとう!

-(void)bar1
{
    NSString* foo = [[NSString alloc] initWithString:@"foo"];
    NSLog(@"%@", foo);

    for (int i=0; i<4; i++) {
    }
}

-(void)bar2
{
    NSString* foo = [[NSString alloc] initWithString:@"foo"];
    NSLog(@"%@", foo);
}

静的文字列のケースは「過度に考案されている」と述べている人もいます。このあまり工夫されていない例は、同じ動作を示しています。

-(void)bar1
{
    NSString* foo = [[NSString alloc] initWithFormat:@"%d",rand()];
    NSLog(@"%@", foo);

    for (int i=0; i<4; i++) {
    }
}

-(void)bar2
{
    NSString* foo = [[NSString alloc] initWithFormat:@"%d",rand()];
    NSLog(@"%@", foo);
}

反復回数が影響することを指摘してくれた方々に感謝します。3 ではリークを報告し、4 では報告しません。これはデッド コードのない新しい例で、反復の違いのみがあります。

リークを報告します:

-(void)bar1
{
    int i=0;
    while (i<3) {
        i++;
    }

    NSString* foo = [[NSString alloc] initWithFormat:@"%d",i];
    NSLog(@"%@", foo);
}

リークを報告しません:

-(void)bar2
{
    int i=0;
    while (i<4) {
        i++;
    }

    NSString* foo = [[NSString alloc] initWithFormat:@"%d",i];
    NSLog(@"%@", foo);
}

私の意見では、この洗練された例は、これがアナライザーのバグであることを明確に示しているため、Apple で DTS チケットをオープンしました。

DTS から、 https: //bugreport.apple.comでバグとして開くように依頼されました。問題 ID 13491388 です。

更新 2013 年 3 月 29 日: Apple は、私のバグ 13491388 がバグ 11486907 の偽物であると報告しています。しかし、バグ 11486907 を開いたり読んだりすることができないため、その情報はまったく役に立ちません。

Apple 開発者サポート FAIL :-(

4

2 に答える 2

3

Clang の静的アナライザーは制御フローに基づいているようです: レポートは常に「このコード パスをたどると、この悪いことが起こる」となります。私の最善の推測は、SA の異なるビット間の望ましくない相互作用であるということです。

  • リーク検出器は、変数が次に書き込まれるとき、または関数が戻るときにのみリークに気付く場合があります。
  • ループを通過する回数には制限があります (検索に永遠に時間がかかるのを避けるため) — ほとんどのループ バグを検出するには、2 回の繰り返しをチェックするだけで十分です。
  • 反復制限に達した時点で、静的アナライザーはまだループを終了しないことを「認識」し (まだi4 未満であるため)、あきらめます。

反復回数を減らすと、リークが検出されると思われます。

最適化を有効にして分析を行うと (スキームを編集する場合、Xcode はそれをサポートしていると思います)、オプティマイザがNSLog()戻り後に変数が無効であると判断し、この情報を SA に使用すると、異なる結果が得られる可能性があります。

問題を報告するとき (コマンド ライン オプションを使用するか、コマンド ラインから直接 clang を実行するか) をどれだけ保守的にするかを調整することもできますが、多くの誤検出を報告せずにこれを行うのは難しい場合があります。

静的分析も、Leaks のようなリーク チェッカーの代わりにはなりません。

于 2013-03-16T03:24:56.480 に答える
3

もう 1 つの答えは順調です。後世のためにもう少し詳しく説明したいと思いました。

リークディテクターは実際にこのリークに気づきます。問題は、リークが重要度​​の低いアナライザー結果と見なされ、リーク後のすべてのパスが「シンク」で終了する場合 (基本的に、分析の異常終了) は報告されないことです。これは、ノイズや偽陽性のリーク レポートを防ぐためです。例えば:

NSString *foo = [[NSString alloc] initWithString:@"foo"];
if (SOME_CONDITION) { 
    NSLog(@"OH NO!");
    exit(-1); 
} else { 
    [foo release]; 
}

これは、条件が true と評価されてもexit実行前にリーク レポートが生成されるため、foo は参照されなくなりますが、まだ所有されているため、リーク レポートが生成されます。(このコードは奇妙に見えますが、通常のアサートでもまったく同じ誤検知がトリガーされます。) (常に) シンクにつながるパスに沿ったリークを抑制することにより、ここで誤検知は生成されません。

残念ながら、「あまりにも多くの」ループを通過すると、アナライザーがあきらめたときにシンクも生成されます。これは、コマンドライン引数によって制御されます-analyzer-max-loop。合格-analyzer-max-loop 5した場合は、サンプル コードでリーク レポートを取得します。

rand()これは、機能するのではなく、ようなものを使用する理由も説明して4います。プログラムの状態を調査するとき、アナライザーは、ループを 1 回も、2 回も、3 回も通過しない場合を考慮します。ループ回数が多いと、シンクが発生しますが、すべてのパスがシンクを通過するわけではないため、まだリークレポートを取得します。(つまり、アナライザーは がi < rand()最初に false であるパスを確認し、関数を終了して、リークを報告します。)

于 2013-03-23T15:04:13.100 に答える