didReceiveMemoryWarning、viewDidUnload和dealloc - didReceiveMemoryWarning:当系统内存不足时,该方法会被调用,开发者可以在该方法中释放一些不必要的资源来减少内存占用。 - viewDidUnload:当视图控制器管理的视图被卸载时,该方法会被调用,开发者可以在该方法中释放与视图相关的资源。 - dealloc:当对象被销毁时,该方法会被调用,开发者可以在该方法中释放对象持有的所有资源。

19

我查看了许多帖子,我的书籍和Apple Developer,并从中获取了大部分所需的理解。如果有善良的人可以确认我是否正确(或更正我),并回答这两个问题,我将不胜感激。

非常感谢,

Chris。

消息顺序 一般而言,消息会按以下顺序出现:

  • didReceiveMemoryWarning

  • viewDidUnload (可能由1引起) - 显然仅适用于视图控制器类。

  • dealloc

didReceiveMemoryWarning

当系统内存不足时调用。

默认情况下,视图控制器已注册内存警告通知,在模板方法中,调用[super didReceiveMemoryWarning]如果视图没有超级视图,则释放视图,这是一种检查视图是否可见的方法。通过将其属性设置为nil来释放该视图。

动作-释放不需要的任何内容,很可能是撤消在viewDidLoad中设置的内容。不要释放UI元素,因为这些应该由viewDidUnload释放。

问题1-即使视图可见也会调用此方法,因此很难确定您可以安全地释放什么。了解这一点以及一些可以释放什么的示例将非常有帮助。

viewDidUnload

每当非可见视图控制器的View属性手动或通常通过didReceiveMemoryWarning设置为nil时都会调用它。

viewDidUnload方法存在的目的是,您可以:

-清理任何其他内容,以节省额外的内存 -如果您保留了一些IBOutlets,则可以帮助释放否则不会被视图卸载释放的内存。

动作-通常,dealloc中释放的任何IBOutlets也应在此方法中释放(并将引用设置为nil)。请注意,如果属性设置为retain,则将其设置为nil也会释放它们。

dealloc

当视图控制器对象被释放时调用,这将在保留计数降至零时发生。

操作 - 释放类已保留的所有对象,包括但不限于所有具有保留或复制属性的对象。

弹出视图控制器和内存

问题2 - 弹出视图会将其从内存中删除吗?


关于问题2:您是指释放视图还是从导航控制器弹出视图控制器? - Robin
@Robin 0 弹出视图控制器。 - Chris
4个回答

31

一些更正和建议:

  • didReceiveMemoryWarning 的实践

正如你所说,控制器的didReceiveMemoryWarning默认实现将释放其视图,如果“安全”释放的话。虽然从苹果的文档中不清楚“安全释放”是什么意思,但通常被认为是它没有父视图(因此没有办法当前可见),并且它的loadView方法可以重新构建整个视图而不会出现问题。

当你重写didReceiveMemoryWarning时最好的实践是根本不尝试释放任何视图对象。如果自定义数据不再需要,请释放它。关于视图,让超类的实现来处理。

然而,有时候数据的必要性可能取决于视图的状态。在大多数情况下,这些自定义数据在viewDidLoad方法中设置。在这种情况下,“安全释放自定义数据”的意思是您知道loadViewviewDidLoad将在视图控制器再次使用自定义数据之前被调用。

因此,在您的didReceiveMemoryWarning中,首先调用超类实现,如果其视图未加载,则释放自定义数据,因为您知道loadViewviewDidLoad将再次被调用。例如,

- (void)didReceiveMemoryWarning {
    /* This is the view controller's method */
    [super didReceiveMemoryWarning];
    if (![self isViewLoaded]) {
        /* release your custom data which will be rebuilt in loadView or viewDidLoad */
    }
}
请小心使用self.view == nil,因为self.view假定该视图对某人有用,并会立即重新加载视图。 viewDidUnload方法
视图控制器由于内存警告而卸载视图时,将调用viewDidUnload。例如,如果您从父视图中删除视图并将控制器的view属性设置为nil,则不会调用viewDidUnload方法。一个微妙的点是,即使视图控制器的视图已经在控制器收到didReceiveMemoryWarning时被释放并设置为nil,因此实际上没有视图可供控制器卸载,如果您调用超类的实现didReceiveMemoryWarningviewDidUnload也将被调用。
这就是为什么手动将视图控制器的view属性设置为nil不是一个好做法。如果您这样做,最好也发送一个viewDidUnload消息。我猜您更希望了解viewDidUnload的含义,但显然这不是当前的行为。
弹出视图控制器
如果您通过“弹出”来指代“从父视图中移除”,它确实会减少视图的保留计数,但不一定会释放它。
如果您指的是从UINavigationController弹出,它实际上会减少视图控制器本身的保留计数。如果视图控制器没有被其他对象保留,它将被释放,最好连同其视图一起释放。正如我所解释的,这次viewDidUnload不会被调用。
其他…
从技术上讲,保留计数可能不会降至零。该对象更有可能在设置计数为零之前被释放。
只是为了确保,默认情况下,视图控制器本身由于内存警告而不会被释放。

@MHC。太棒了,真的很好能对此有一个牢固的理解,现在我知道如何区分那些被隐藏的视图了。我相信这也会帮助其他人。谢谢。 - Chris
是的,viewDidUnload总是在didReceiveMemoryWarning之后调用。 - MHC
不要在viewDidUnload方法中释放数据,因为不能保证每次视图卸载时都会调用该方法。如果你想处理视图被其他对象显式卸载的情况,你需要在其他地方处理它。 - MHC
@MHC - 谢谢 - 所以听起来我应该只是在didReceiveMemoryWarning中通常这样做。另外 - 如果我在didReceiveMemoryWarning中释放不是属性的对象,我是否也需要将其设置为nil? - Chris
当视图仍在加载时,可能会调用didReceiveMemoryWarning吗? - kbtz
显示剩余3条评论

8

didReceiveMemoryWarning

...

动作 - 释放任何不需要的东西,可能是撤销你在viewDidLoad中设置的任何内容。

这是错误的。在viewDidLoad中重新创建的任何东西都应该在viewDidUnload中被释放(并设置为nil)。正如您在下面提到的那样,didReceiveMemoryWarning也会在视图可见时调用。在didReceiveMemoryWarning中,您应该释放像缓存或其他您正在保留的视图控制器之类的东西,这些东西可以在下次需要时懒惰地重新创建(即,通过手动实现它们的getter方法)。

viewDidUnload

...

动作 - 通常在dealloc中释放的任何IBOutlets,在此方法中也应该被释放(并将引用设置为nil)。请注意,如果属性被设置为retain,则将它们设置为nil也会释放它们。

正确。通常,在viewDidLoad中创建的所有东西以及所有声明为retain的IBOutlets都应该在此处被释放并设置为nil

dealloc

...

动作 - 释放类所保留的所有对象,包括但不限于所有具有retain或copy属性的属性。

正确。值得注意的是,这包括您在viewDidUnload中处理的所有对象,因为后者在dealloc过程中不会被隐式调用(据我所知)。这就是为什么在viewDidUnload中将所有释放的对象设置为nil至关重要,否则您可能会出现两次释放的情况(首先在viewDidUnload中,然后在dealloc中再次释放;如果您将指针设置为nil,则dealloc中的释放调用将无效)。

弹出视图控制器和内存

问题2 - 弹出视图是否会将其从内存中删除?

不一定。这是您不必担心的实现细节。无论当前的做法是什么,苹果都可以在下一个版本中更改它。


感谢您的澄清和纠正。现在我可以自信地去整理我的代码了。 - Chris

3

0

从iOS 6开始,我们如何检查视图是否重新加载。由于“viewDidUnload”已被弃用。您确定如果视图在“didReceiveMemoryWarning”警告后被移除,“loadView”和“viewDidLoad”会被调用吗?


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