iPhone内存管理和发布

时间:2009-05-05 02:03:52

标签: iphone objective-c cocoa memory-management nsview

这是我经常看到的一种常见做法(包括来自非常受欢迎的iPhone开发者书籍)

在.h文件中:

@interface SomeViewController : UIViewController
{
  UIImageView *imgView;
}

.m文件中的某处:

imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen]
applicationFrame]];
[imgView setImage:[UIImage imageNamed:@"someimage.png"]];
[self addSubview:imgView];
[imgView release];

后来,我们看到了......

- (void) dealloc
{
  [imgView release];
  [super dealloc];

} 

由于imgView具有匹配的alloc和release,是否需要在dealloc中发布imgView?

addSubview调用保留的imgView在哪里占了?

6 个答案:

答案 0 :(得分:9)

代码不正确。在解除分配后,您最终会发布imgView

在您的.m文件中,您:

  1. alloc它 - >你拥有它
  2. 将其添加为子视图 - >你和the UIView owns it
  3. release它 - >你不拥有它
  4. 然后在dealloc中,您release imgView即使我们在上面的步骤3中建立了您不拥有它。当您致电[super dealloc]时,视图会释放所有子视图,我想您会遇到异常。

    如果您想保留imgView的ivar,我建议在您将其添加为子视图后调用release,并保留dealloc相同。这样,即使imgView在某个时候从视图层次结构中删除,您仍然会有一个有效的引用。

答案 1 :(得分:0)

代码不正确,你不应该在init方法中释放它,就像调用dealloc一样(如果你想把它保存为ivar,你不需要除非你需​​要一个指向它的指针)其他地方,因为addSubview:将为你保留视图)。

我相信它实际上没有崩溃的原因是因为它仍然被超类(从调用addSubview :)保留,所以当它在dealloc中发布时实际上是平衡的。该视图可能会在超级视图后立即从超级视图中删除,因此当调用[super dealloc]时,它不会被过度释放。这是我的预感,至少。

答案 2 :(得分:0)

init中的释放不正确。

你提到了“普通的做法”和一本未命名的书。我建议查看Apple的规范示例:ViewTransitions就是这种情况的一个很好的例子(并且有2个视图可以启动;)

http://developer.apple.com/iphone/library/samplecode/ViewTransitions/index.html

答案 3 :(得分:0)

基本答案是,示例代码中只应该有一个[imgView release]是在addSubview之后还是在dealloc 之后)。不过,我会从[imgView release]中移除dealloc,然后将其留在addSubview之后。

iPhone上有一个问题;使用didReceiveMemoryWarning,您可以从您下面释放对象(包括整个视图)。如果您有一个应用程序范围的保留集并且您不尊重内存,那么您可能会发现该应用程序只是被杀死。

一个很好的例子是:
如果你想到一组嵌套的3个视图,请查看1->视图2->查看3。 接下来,考虑“viewDidLoad”和“viewDidUnload”来电。如果用户当前处于“查看3”状态,则View1可能会被卸载,这就是令人讨厌的地方。
 如果在viewDidLoad中分配了一个对象,并且在将其添加到子视图后没有释放它,则在卸载view1时不会释放您的对象,但是仍然会卸载view1。  viewDidLoad将再次运行,您的代码将再次运行,但现在您已经有两个对象实例化而不是一个;一个对象将在以前卸载的视图中处于无处,并且新对象将用于当前可见的视图。冲洗,泡沫和重复,你会发现你的应用程序因内存泄漏而崩溃。

在此示例中,如果给定的代码块是易失性的并且有可能再次执行(无论是因为内存还是未加载的视图),我会从中删除[imgView release]; dealloc并在addSubView之后保留它。

以下是基本保留/发布概念的链接: http://www.otierney.net/objective-c.html#retain

答案 4 :(得分:0)

(我没有足够的声誉来添加评论。)

@bentford:如果我错了,请纠正我,但我相信为了使用imgView属性的合成setter,你必须使用“self.imgView”:

self.imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen]

如果你没有 self。,它只是使用了ivar,并没有得到额外的保留。

答案 5 :(得分:-1)

是的,该代码存在问题。它过早地释放imgView,这可能会在极少数情况下导致崩溃将一个对象存储在一个实例变量中而不保留它,而且它通常只是以错误的方式进行内存管理。

执行此操作的一种正确方法是:

@interface SomeViewController : UIViewController
{
    UIImageView *imgView;
}
@property (nonatomic, retain) UIImageView *imgView;

并在实施中;

@synthesize imgView;

模块中的某个地方:

//Create a new image view object and store it in a local variable (retain count 1)
UIImageView *newImgView = [[UIImageView alloc] initWithFrame:self.view.bounds];
newImgView.image = [UIImage imageNamed:@"someimage.png"];

//Use our property to store our new image view as an instance variable,
//if an old value of imgView exists, it will be released by generated method,
//and our newImgView gets retained (retain count 2)
self.imgView = newImgView;

//Release local variable, since the new UIImageView is safely stored in the
//imgView instance variable. (retain count 1)
[newImgView release];

//Add the new imgView to main view, it's retain count will be incremented,
//and the UIImageView will remain in memory until it is released by both the
//main view and this controller. (retain count 2)
[self.view addSubview:self.imgView];

dealloc保持不变:

- (void) dealloc
{
    [imgView release];
    [super dealloc];
}