2013-03-15 36 views
3

下面的两个方法都分配一个NSString并将其泄漏。运行XCode(4.6)Anaylzer成功标记bar2中的泄漏,但在bar1中没有提及它。XCode(4.6)分析仪 - 为什么在一种情况下检测到泄漏而不是另一种?

我不知道为什么。

在我的实际项目中,我们发现,我们希望在像在BAR2一个明显的方式来抓住了泄漏,但它不是,因为在BAR​​1相同的行为的发现。

请帮我理解为什么。谢谢!

-(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); 
} 

我已经开了DTS门票与苹果,作为这个精致的例子,在我看来,清楚地表明,这是一个分析仪的错误。

DTS要求我打开它与https://bugreport.apple.com至极我做了一个错误。这是问题编号13491388.

更新2013年3月29日:苹果 报告说我的错误13491388是错误的欺骗11486907. 但是,我不能打开或阅读错误11486907东西,这样的信息是完全无用的。

苹果开发者支持失败:-(

+0

在Xcode 4.6上,分析器使用警告'Using initWithString:with literal'标记代码多余的“,但没有提到任何泄漏。如果启用ARC,我不会看到任何泄漏。 – 2013-03-15 20:00:26

+0

在该项目中,ARC被禁用。 – 2013-03-15 20:22:42

+0

另外,是的,这里使用initWithString是愚蠢的,但它只是一个人为的例子。另一个使用init的alloc显示了同样的问题。 – 2013-03-15 20:23:20

回答

3

锵的静态分析似乎是基于控制流:该报告始终是“如果你按照这个代码路径,这种不好的事情发生”我最好的猜测是,它的SA的不同位之间的不必要的相互作用:

  • 检漏仪可能只注意到当变量旁边写着或函数返回的泄漏
  • 有它多少次经过的限制循环(避免永久搜索)— ch重复两次迭代应该足以检测大多数循环错误。
  • 在它击中迭代极限点,静态分析“知道”,它不会退出循环,但(因为i仍低于4),所以它放弃。

我怀疑减少迭代次数使待检测的泄漏。

如果优化器确定变量在NSLog()返回并将此信息用于SA后确定变量已死,则启用优化时执行分析(我认为Xcode支持如果编辑该方案)可能会给出不同的结果。

当报告问题(通过命令行选项,或直接从命令行运行clang?)时,您可能还会调整它的保守程度吗?但是,如果没有标记很多问题误报。

静态分析也没有用于使用泄漏检查等泄漏的替代品。

+0

将“分析”配置设置为“发布”并没有什么区别。如果将迭代计数减少到3,实际上检测到泄漏,但是如果迭代计数未知('for(int i = 0; i 2013-03-16 11:19:24

+0

@MartinR如果迭代计数未知,那么*可能*为0(或1或2);我怀疑在报告的“可能的泄漏”中它是0。我不确定Clang是否会尝试一些边缘案例返回值或更复杂的东西。 – 2013-03-18 13:25:30

3

对方回答是正确的轨道上;我想为后人提供更多的细节。

检漏仪实际上确实通知此泄漏。问题在于泄漏被认为是重要性低的分析仪结果,并且如果泄漏之后的每个路径结束于“汇”(基本上,分析的异常终止),则不会报告泄漏。这是为了防止嘈杂或假阳性泄漏报告;例如:

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

这将产生泄漏报告,因为如果条件计算为真,但在此之前exit运行,就不会有泄漏报告,因为富不再被引用,但仍拥有。 (这段代码看起来很奇怪,但正常的断言也会触发完全相同的误报。)通过抑制(总是)导致汇的路径的泄漏,这里不会产生误报。

不幸的是,通过一个循环去“太多”时间也产生下沉时,分析仪放弃。这由命令行参数-analyzer-max-loop控制;如果您通过-analyzer-max-loop 5,那么您将通过示例代码获得泄漏报告。

这也解释了为什么使用类似rand()而非4作品;探索程序状态时,分析仪认为,我们通过循环没有时间,一次,两次,三次,等任何更高的循环计数导致汇去的情况,但因为没有所有路径经过水槽,你仍然得到泄漏报告。 (即分析器首次看到i < rand()错误的路径,我们离开该功能,因此报告泄漏)。

相关问题