静态分析仪显示错误泄漏? (XCode 4.0、iOS 4.3 及更高版本)

发布于 2024-12-13 10:21:37 字数 1180 浏览 0 评论 0原文

祝大家十一月快乐,

我在我的项目上尝试了 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>];

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

编辑:那里有泄漏吗?

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

Happy November to all,

Well I tried Xcode Build and analyze on my project, and it showed some unusual leaks, which I couldn't quite accept with my knowledge of Objective C.

So I decided to put up a test project and ask here..

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

When I try Build and analyze, clang static analyzer say

Potential leak of an object at line xx

And the culprit line is

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

I think I am releasing once for every time I am allocing/retaining. Am I missing something, or Static analyzer has some bugs?

EDIT : Is there any leak there?

Well I run the above project using Leak tool in instrument..It didn't show any leak even though I tried many times..Whom should I believe? Static analyzer or Leak instrument?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

丘比特射中我 2024-12-20 10:21:38

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

- (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->ivar 关联不应该是可信:

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

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

1)通过 setter 分配图像视图
2)通过getter访问图像视图
3)直接访问ivar,当添加到self.view时

  • ,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查看您通过的。在这种情况下,您传递的变量视图将泄漏,并且替换它的视图将出现负不平衡。

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

编辑:那里有泄漏吗?

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

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

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

如果没有泄漏,也没有僵尸,那么就不存在泄漏。但解决方案很容易解决——而且程序在开发过程中会发生变化。使用我发布的表格要容易得多,因此工具集和您验证程序是否正确都更容易。静态分析器并不总是正确的,但是您应该调整您的程序以使其满意,因为静态分析非常有用。我发布的程序也更容易让人理解并确认它是正确的。

your problem is how you release it:

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

you should do it this way:

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

The checker is correct because it cannot assume that the variable is identical to the one you set. Therefore, the form you use in the OP could introduce a reference count imbalance because the ivar's value may not be what you assigned to it by the time you message release upon the ivar.

These cases are not likely for a UIImageView, and quite unlikely in the context of your program, but these examples should give you an idea as to why the checker assumes that object->ivar associations shall not be trusted:

Between creation of the image view and the message to release it via the ivar, you have:

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

1) assignment of the image view via the setter
2) access of the image view via the getter
3) direct access of the ivar, when adding to self.view

  • the setter may have taken a copied or used a cached value. UIImageView is a bad example, but the checker does not know how types are generally passed around - even if it did, it would (at times) make unsafe assumptions.

the simplest example would be:

- (void)setName:(NSString *)inName {
  NSString * prev = name;
  if (inName == prev) return;
  if (0 == [inName count]) name = @"";
  else name = [inName copy];
  [prev release];
}
  • the value held by the ivar could change in the meantime. not likely an issue in this case, but let's say that adding the image view as the subview could end up calling back and altering self in the process/effect of adding the subview, and replacing or removing the image view you passed. In that case, the variable view you passed would leak and the view it replaced it with would have a negative imbalance.

Neither of those are likely to happen in your example, but it does happen in real world programs, and the checker is correctly evaluating based on locality, not property (the checker can't assume much of what happens inside a method call). It also encourages one good idiomatic style in this case.

EDIT : Is there any leak there?

Well I run the above project using
Leak tool in instrument..It didn't shown any leak even though I tried
it many times..Whom should I believe? Static analyzer or Leak
instrument?

The static analyzer says there is a potential leak because it is unable to guarantee the reference/allocation it follows is correctly retained/released. You can guarantee that reference counting is correct and please the static analyzer by changing you program to look like I wrote it in my example.

The way you have written it has made it impossible for the analyzer to follow the reference.

If you have no leaks and no zombies, then there is not a leak. But the solution is easy to fix - and programs have a way of changing during development. It's much easier to use the form I posted so it is easier for the toolset and for you to verify the program is correct. The static analyzer is not always correct, but you should adjust your programs to please it because static analysis is very useful. The program I posted is also easier for a human to understand and confirm that it is correct.

谜泪 2024-12-20 10:21:38

当您像这样使用保留声明属性时,

@property(nonatomic,retain) UIImageView *tstImageView;

会添加一个设置器,当您分配给该属性时,该设置器将增加保留计数。当您执行以下操作时,您创建的对象已经有一个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;

因为设置器将正确设置保留计数

when you declare a property with retain like this

@property(nonatomic,retain) UIImageView *tstImageView;

a setter is added that will incr the retainCount when you assign to the property. When you do as below the object you created has already a retainCount == 1

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

so the tstImageView object has 2 in retainCount.

do instead

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

then, although unrelated to your leak when you release it write like this instead

self.tstImageView = nil;

since the setter will then will properly set the retainCount

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文