静态分析仪显示错误泄漏? (XCode 4.0,iOS 4.3及以上版本)

时间:2011-11-03 06:18:31

标签: iphone objective-c ios memory-leaks clang-static-analyzer

祝大家十一月快乐,

我尝试过Xcode Build并对我的项目进行分析,它显示了一些不寻常的泄漏,我对Objective C的了解无法接受。

所以我决定提出一个测试项目并在这里问...

MemoryTestController.h

@interface MemoryTestController : UIViewController{
  UIImageView *tstImageView;
}
@property(nonatomic,retain) UIImageView *tstImageView;
@end

MemoryTestController.m

@implementation MemoryTestController
@synthesize tstImageView;

- (void)viewDidLoad{
  [super viewDidLoad];

  self.tstImageView  = [[UIImageView alloc] //<==This object is leaking
                           initWithFrame:<SomeFrame>]; 
  self.tstImageView.image = [UIImage imageNamed:@"SomeImage.png"];
  [self.view addSubview:tstImageView];
  [tstImageView release];
}

-(void)dealloc{
  [tstImageView release];
  [super dealloc];
}
@end

当我尝试构建和分析时,clang静态分析器说

  

第xx行的对象潜在泄漏

罪魁祸首是

self.tstImageView  = [[UIImageView alloc]initWithFrame:<SomeFrame>];

我想我每次分配/保留时都会释放一次。我错过了什么,或静态分析器有一些错误?

编辑:那里有泄漏吗?

我使用仪器中的泄漏工具运行上述项目。即使我多次尝试也没有显示任何泄漏......我应该相信吗?静态分析仪或泄漏仪器?

2 个答案:

答案 0 :(得分:5)

你的问题是你如何释放它:

- (void)viewDidLoad{
  [super viewDidLoad];

  self.tstImageView  = [[UIImageView alloc] //<==This object is leaking
                           initWithFrame:<SomeFrame>]; 
  self.tstImageView.image = [UIImage imageNamed:@"SomeImage.png"];
  [self.view addSubview:tstImageView];
  [tstImageView release]; // << here
}

你应该这样做:

- (void)viewDidLoad{
  [super viewDidLoad];

  UIImageView * imageView  = [[UIImageView alloc] initWithFrame:<SomeFrame>]; 
  imageView.image = [UIImage imageNamed:@"SomeImage.png"];
  self.tstImageView  = imageView;
  [imageView release];
  [self.view addSubview:self.tstImageView];
}

检查器是正确的,因为它不能假设变量与您设置的变量相同。因此,您在OP中使用的表单可能会引入引用计数不平衡,因为ivar的值可能不是您在ivar上发布消息时分配给它的值。

这些情况不太可能是UIImageView,并且在您的程序环境中不太可能,但这些示例应该让您了解为什么检查程序假定object-&gt; ivar关联不应该是可信:

在创建图像视图和通过ivar释放它的消息之间,你有:

  self.tstImageView  = [[UIImageView alloc] initWithFrame:<SomeFrame>]; 
  self.tstImageView.image = [UIImage imageNamed:@"SomeImage.png"];
  [self.view addSubview:tstImageView];

1)通过setter分配图像视图 2)通过getter访问图像视图 3)添加到self.view

时直接访问ivar
  • setter可能已经复制或使用了缓存值。 UIImageView是一个不好的例子,但是检查器不知道类型通常是如何传递的 - 即使它确实存在,它也会(有时)做出不安全的假设。

最简单的例子是:

- (void)setName:(NSString *)inName {
  NSString * prev = name;
  if (inName == prev) return;
  if (0 == [inName count]) name = @"";
  else name = [inName copy];
  [prev release];
}
  • 伊塔尔持有的价值可能会在此期间发生变化。在这种情况下不太可能出现问题,但是假设将图像视图添加为子视图可​​能最终会在添加子视图的过程/效果中回调和更改self,并替换或删除您传递的图像视图。在这种情况下,您传递的变量视图会泄漏,而替换它的视图会产生负面不平衡。

这些可能在你的例子中都不会发生,但它确实发生在现实世界的程序中,并且检查器正在根据地点而不是属性正确评估(检查员不能假设太多在方法调用中发生的事情)。在这种情况下,它也鼓励一种良好的习惯风格。

  编辑:那里有泄漏吗?

     

我运行上面的项目   仪器中的泄漏工具。即使我尝试过,也没有显示任何泄漏   很多次......我应该相信吗?静态分析仪或泄漏   仪器

静态分析器说存在潜在的泄漏,因为它无法保证正确保留/释放它所遵循的参考/分配。您可以保证引用计数是正确的,并通过将程序更改为我在我的示例中编写的程序来请静态分析器。

您编写的方式使分析仪无法遵循参考。

如果您没有泄漏且没有僵尸,那么就没有泄漏。但是解决方案很容易解决 - 程序在开发过程中有改变的方法。使用我发布的表单要容易得多,因此工具集更容易验证程序是否正确。静态分析仪并不总是正确的,但您应该调整程序以取悦它,因为静态分析非常有用。我发布的程序也更容易让人理解并确认它是正确的。

答案 1 :(得分:1)

当您声明具有此类保留的属性时

@property(nonatomic,retain) UIImageView *tstImageView;

添加了一个setter,当您分配给该属性时,该setter将包含retainCount。当你这样做时,你创建的对象已经有一个retainCount == 1

self.tstImageView  = [[UIImageView alloc] 
                           initWithFrame:<SomeFrame>];

所以tstImageView对象在retainCount中有2个。

改为

UIImageView* view = [[UIImageView alloc] initWithFrame:<SomeFrame>];
self.tstImageView  = view;
[view release];

然后,虽然你在发布它时泄漏与你的泄漏无关而是写这样的

self.tstImageView = nil;

因为setter将正确设置retainCount