如何禁止Xcode Clang静态分析器警告?

5
"可能存在一个对象泄漏,该对象在第n行分配并存储在“变量”中。"这通常是非常有用的分析器警告,但有些情况下我会得到令人讨厌的误报,我希望能够消除它们以保持我的分析器结果干净。在分析器的防御下,如果不是因为另一条执行路径中的释放(对此它是盲目的),它所注意到的肯定是内存泄漏。
我将详细说明我的情况。它以各种风格发生,但一般模式如下:
1.分配对象并设置其委托。 2.对对象进行某些操作。(启动任务,显示视图等) 3.当前方法的执行结束。(进入Clang警告)。 4.对象决定其任务已完成,向委托发送消息。 5.委托释放对象。
这绝不是一个奇特的设计模式,因此我希望可以消除它。我知道可以通过将有问题的对象存储在稍后释放的ivar中来避免这种情况,但我非常不希望添加ivar污染。"

我觉得一个对象的代理保留它很奇怪。通常,一个对象会保留它的代理,而让代理保留对象会导致保留循环。我并不是说没有零散的情况下代理可能想要保留(也许是短暂的)对象,但似乎了解为什么在这种情况下会这样做很重要。从这里所说的内容来看,我认为让对象在操作和随后的代理消息期间保留自己比让代理“拥有”alloc/init隐含的保留更加合适。 - ipmcc
@ipmcc:虽然一个对象保留其委托是可能的,在SDK中有一些类就是这样做的,但我认为更常见的是将委托存储为弱引用,以避免您提到的保留循环。无论哪种方式,一个对象保留其委托绝对不是“典型”的。在我的情况下,某个类使用alloc/init创建了一个对象,并将自己设置为委托。作为该类的所有者,委托在完成后释放对象。这里没有人为的所有权发生。 - Matt Wilding
@ipmcc - Matt 是正确的,通常的模式是:对象 A 创建对象 B 并将自己设置为对象 B 的代理。对象 A 保留对象 B。当对象 A 被释放时,它确保取消设置自己作为对象 B 的代理,否则可能会导致崩溃。 - DougW
是的,我在这里有点说错了。对于大多数情况,Matt Wilding 和 DougW 的评论是正确的。尽管如此,存在一些奇怪的情况,并且任何东西都可以保留其他任何东西 - 在委托模式中没有实施保留/释放行为。 - ipmcc
@ipmcc - 非常正确,你应该始终“信任但要验证”。去年我在WWDC上与一位苹果开发者进行了长时间的讨论,因为UIView的setAnimationDelegate实际上会保留委托,并且当时没有这样说明(也没有取消的方法)。他似乎并不认为这是一个大问题,但我注意到文档已经更新,所以显然有人认为这很重要。 - DougW
3个回答

6

命名有点误导,实际上它的意思是Cocoa API注释。而ns_consumed是相对较新的。我不认为Clang 1.6有它们(随Xcode 3.2一起提供的版本),但我希望Clang 2.0有(我还没有测试过)。 - Lily Ballard
我安装了新的分析器,但源代码注释仍然没出现。 - Matt Wilding
@Kevin Ballard - 虽然这是一些情况下的好建议,但我投票反对它,因为它在这里绝对不合适。他的保留/释放循环存在问题,应该进行修复,而不是被抑制。 - DougW
@DougW:你为什么这么说?OP明确表示这是一个误报,并且他正在通过另一条执行路径确保对象被释放。是的,这可能表明架构不佳,而且很可能他没有深思熟虑,某些情况下可能会导致对象泄漏,但你没有他的代码,也不知道这是否属实。 - Lily Ballard
@DougW:这解决了所提出的问题。我的架构是否完全失败与此答案的有效性并不真正相关;将来其他人可能会发现需要抑制警告的有效情况,而这个答案和任何其他答案一样有效。 - Matt Wilding
显示剩余10条评论

1

我认为在这种情况下,你应该注意静态分析器的信息。你的模式存在潜在问题。

具体来说,当你从执行步骤5的方法返回时,你处于一个可能已经被释放的对象的方法中。我理解你的模式类似于这样:

// steps 1, 2, 3
-(void) methodThatCreatesObject
{
    id theObj = [[TheObj alloc] init];
    [theObj setDelegate: delegateObj];
    // other stuff
}

请注意,上述违反了内存管理规则

// step 4 - a method of theObj
-(void) someMethod
{
    [delegate notifyTaskCompleteFromObj: self];
    // self points to an invalid object here.  Doing anything with self results in EXC_BAD_ACCESS
}

以上违反了内存管理规则中的一个假设:
收到的对象通常保证在接收它的方法中保持有效。
如果我们说self是一个收到的对象,从技术上讲,它确实是,因为它作为堆栈上的参数传递。
// step 5 the delegate method defined in the delegate object

-(void) notifyTaskCompleteFromObj: (TheObj*) anObj
{
    // do stuff
    [anObj release];
}

上述内容也违反了内存管理规则。

正常的模式是拥有控制器同时拥有委托和具有委托的对象(通常控制器本身就是委托)。我认为你应该转向这种模式。


如果那是我的模式,我会同意你的观点。我想我的概括留下了太多的解释空间。这里有一个具体的例子,说明了正在发生的事情:在控制器对象的方法中,另一个对象(自定义网络请求)被分配,并且它的代理被设置为self,而不是另一个对象。因此,控制器拥有它作为代理的对象。稍后,在从网络请求接收到通知时,控制器释放该请求。没有违规;我没有将责任移交给另一个对象。 - Matt Wilding
@MattпјҡдҪ жҳҜеңЁиҜҙжҲ‘дҫӢеӯҗдёӯзҡ„theObjжҳҜдёҖдёӘе®һдҫӢеҸҳйҮҸеҗ—пјҹ - JeremyP
不,不是这样的。我可以将其作为实例变量保留引用以便稍后释放,但由于theObj在委托消息中传递了对自身的引用,因此我选择释放作为消息参数传递的引用,而不是添加一个实例变量来释放它。然而,正是缺少对theObj的引用,导致分析器不喜欢这段代码。 - Matt Wilding
1
@Matt Wilding - Jeremy 在这里是正确的。仅仅因为你在稍后可能会或可能不会回调委托时理论上平衡了你的保留/释放,并不意味着它是正确的用法。你需要在创建的每个作用域结束时平衡保留/释放计数。除非你在一个显式返回对象给调用者的方法中,否则你永远不应该在没有为每个使用的对象(考虑自动释放)平衡保留/释放计数的情况下离开方法作用域。 - DougW

0

我想到了另一个有趣的选项。OP提供了以下场景:

  1. 分配了一个对象并设置了它的代理。
  2. 对该对象执行某些操作(启动任务、显示视图等)。
  3. 当前方法的执行结束。(进入Clang警告)。
  4. 对象决定其任务已完成,向代理发送消息。
  5. 代理释放对象。

如果您不想显式释放对象,而是希望将分配的对象的生命周期延长到分配/代理对象的生命周期,可以这样做:

TheObject* foo = [[TheObject alloc] init] autorelease];
foo.delegate = self;
[foo doSomething];
objc_setAssociatedObject(self, foo, foo, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return;

通过使用保留策略将foo设置为关联对象,委托方(self)将有效地对该对象进行保留,该对象将在委托方(self)稍后被释放时随之释放。
这不完全是OP所要求的,但它仍然是一个有用的模式,并且感觉在OP提出的情况下可能已经足够了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接