2

祝大家十一月快乐,

好吧,我在我的项目上尝试了 Xcode 构建和分析,它显示了一些不寻常的泄漏,以我对 Objective C 的了解,我不能完全接受。

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

内存测试控制器.h

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

内存测试控制器.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>];

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

编辑:那里有泄漏吗?

好吧,我使用仪器中的泄漏工具运行上述项目..即使我尝试了很多次,它也没有显示任何泄漏..我应该相信谁?静态分析仪还是泄漏仪?

4

2 回答 2

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 上发布消息时分配给它的值。

这些情况对于 aUIImageView来说不太可能,并且在您的程序上下文中也不太可能,但是这些示例应该让您了解为什么检查器假定 object->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];
}
  • 同时,ivar 持有的价值可能会发生变化。在这种情况下不太可能是一个问题,但是假设将图像视图添加为子视图可​​能最终会回调并更改self添加子视图的过程/效果,并替换或删除您传递的图像视图。在这种情况下,您传递的变量视图将泄漏,并且它替换它的视图将具有负不平衡。

这些都不太可能在您的示例中发生,但它确实发生在现实世界的程序中,并且检查器正在根据位置而不是属性正确评估(检查器不能假设方法调用中发生的大部分事情)。在这种情况下,它还鼓励一种良好的惯用风格。

编辑:那里有泄漏吗?

好吧,我使用仪器中的泄漏工具运行上述项目..即使我尝试了很多次,它也没有显示任何泄漏..我应该相信谁?静态分析仪还是泄漏仪?

静态分析器表示存在潜在泄漏,因为它无法保证其遵循的引用/分配被正确保留/释放。你可以保证引用计数是正确的,并通过改变你的程序看起来像我在我的例子中写的那样取悦静态分析器。

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

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

于 2011-11-03T06:24:45.467 回答
1

当您像这样声明带有保留的属性时

@property(nonatomic,retain) UIImageView *tstImageView;

添加了一个 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;

因为设置器将正确设置retainCount

于 2011-11-03T06:24:17.397 回答