OBJ-C: 如何释放从方法返回的对象?

3

我对从方法中返回的对象的内存警告感到困惑。 这是我的代码。

-(void)returnHeaderView
{
  self.headerView=[[UIView alloc]init];
  headerView.frame=CGRectMake(0, 0, 955, 45);


  UILabel *fromLabel=[self returnLabel];
  fromLabel.frame=CGRectMake(400, 5, 200, 44);
  fromLabel.text=@"Open Time";
  [headerView addSubview:fromLabel];
  [fromLabel release];(in correct decrement of the reference count of an object that is not owned at this point by the caller)

 [self.headerView addSubview:fromLabel];
 [self.view addSubview:self.headerView];
 [self.headerView release];

}

-(UILabel *)returnLabel
{
UILabel *label= [[UILabel alloc] init] ;
label.textColor = [UIColor blackColor];
label.font = FONT_TITLE;
label.numberOfLines=1;
label.textAlignment=UITextAlignmentLeft;
label.lineBreakMode=UILineBreakModeWordWrap;
label.backgroundColor=[UIColor clearColor];

return label;

}

这是我的两个方法: 1. - (void)returnHeaderView。 2. - (UILabel *)returnLabel。
-returnLabel 返回一个UILabel对象的引用,该引用被传递给returnHeaderView方法中的fromLabel UILabel对象。然后我释放了fromLabel对象。
但是这会导致内存警告(在此时未由调用方拥有的对象的引用计数不正确地减少)。
因此,有人能建议我代码中有什么问题吗? 以及如何释放由方法返回的对象。
谢谢。

您可以参考以下链接:https://dev59.com/wlXTa4cB1Zd3GeqPzjyk - jkk
苹果公司的《高级内存管理编程指南》是所有Objective-C程序员必读的资料。它完整地回答了这个问题以及许多其他重要的内存管理问题。 - Caleb
4个回答

3
在你的returnHeaderView方法中,你实际上没有保留fromLabel,因此你不拥有它,也不应该在那里释放它(警告“错误地减少了调用者此时未拥有的对象的引用计数”)。
最好的选择是将返回的标签自动释放:return [label autorelease];,并让它在周围的自动释放池被清空之前保持活动状态。请注意,这也意味着从你的returnHeaderView方法中删除[fromLabel release];调用。
如果这听起来很复杂,请使用ARC,因为它实际上是推荐的。

2
请进行小的更改并删除泄漏,并将self.headerView设置为自动释放以消除其他内存泄漏警告:
-(void)returnHeaderView
{
  self.headerView=[[[UIView alloc]init] autorelease];
  headerView.frame=CGRectMake(0, 0, 955, 45);

  UILabel *fromLabel = [[self returnLabel] retain];
  fromLabel.frame    = CGRectMake(400, 5, 200, 44);
  fromLabel.text     = @"Open Time";
  [headerView addSubview:fromLabel];
  [self.headerView addSubview:fromLabel];
  [self.view addSubview:self.headerView];
  //[self.headerView release];
}

-(UILabel *)returnLabel
{
  UILabel *label= [[UILabel alloc] init] ;
  label.textColor = [UIColor blackColor];
  label.font = FONT_TITLE;
  label.numberOfLines=1;
  label.textAlignment=UITextAlignmentLeft;
  label.lineBreakMode=UILineBreakModeWordWrap;
  label.backgroundColor=[UIColor clearColor];
  return [label autorelease];
}

1

您可以通过更改代码来避免泄漏,例如:

-(void)returnHeaderView
{
  self.headerView=[[UIView alloc]init];
  headerView.frame=CGRectMake(0, 0, 955, 45);


  UILabel *fromLabel = [[self returnLabel] retain];
  fromLabel.frame    = CGRectMake(400, 5, 200, 44);
  fromLabel.text     = @"Open Time";
  [headerView addSubview:fromLabel];
  [self.headerView addSubview:fromLabel];
  [self.view addSubview:self.headerView];

  [fromLabel release];
  [self.headerView release];

}

-(UILabel *)returnLabel
{
UILabel *label= [[UILabel alloc] init] ;
label.textColor = [UIColor blackColor];
label.font = FONT_TITLE;
label.numberOfLines=1;
label.textAlignment=UITextAlignmentLeft;
label.lineBreakMode=UILineBreakModeWordWrap;
label.backgroundColor=[UIColor clearColor];

return [label autorelease];

}

建议:避免这种泄漏的最佳方法是使用ARC。

如果他在returnLabel中添加自动释放,甚至不需要保留和释放。如果他选择这种方式,应该只需添加自动释放,以便returnLabel返回一个自动释放的对象,然后在returnHeaderView中不需要保留或释放fromLabel - Aaron Golden
但在 OP 发布的代码中,自动释放池肯定不会在 returnLabel 返回后立即被清空! - Aaron Golden
@AaronGolden:是的,那样做可以。但这不是推荐的方式。始终保留返回的自动释放对象(拥有所有权)。苹果文档推荐这种方法。 - Midhun MP
@AaronGolden 请看这段代码:UILabel *fromLabel = [self returnLabel]; 这里的 fromLabel 是一个自动释放的对象。他在改变它的frame、添加文本和许多其他的事情都是在一个自动释放的对象上进行的。你无法预测什么时候一个自动释放的对象会被释放。而且调用函数没有对象的所有权。当你将它作为子视图添加时,没问题(视图会保持它)。如果你这样做:[self.view addSubview:[self returnLabel]] 没问题。否则最好的方法是在使用后保留自动释放的对象并释放它。这是推荐的做法! - Midhun MP
你可以绝对预测何时会释放自动释放池。如果附近没有@autoreleasepool或显式创建NSAutoreleasePool,则处理的是在当前运行循环迭代开始时创建并在当前运行循环迭代结束时释放的池。你认为会发生什么,自动释放池会被后台线程释放吗?如果是这种情况,即使从returnLabel返回后立即保留,也无法避免竞争条件。 - Aaron Golden
显示剩余2条评论

1
我觉得你的returnLabel方法返回的对象保留计数为1,而不是编译器所建议的0。我认为你可能只需要更改方法名称,以便给编译器关于返回对象所有权的提示。尝试将returnLabel更改为createReturnLabel。然后该方法将符合复制/创建命名约定

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