didReceiveMemoryWarning、viewDidUnload 和 dealloc

Posted

技术标签:

【中文标题】didReceiveMemoryWarning、viewDidUnload 和 dealloc【英文标题】:didReceiveMemoryWarning, viewDidUnload and dealloc 【发布时间】:2011-07-01 11:54:56 【问题描述】:

我浏览了很多帖子、我的书籍和 Apple Developer,并收集了我在使用这些方面需要的大部分理解。如果有好心人能确认我做对(或纠正我)并回答这两个问题,我将不胜感激。

非常感谢,

克里斯。

消息顺序 通常,消息将按以下顺序出现:

didReceiveMemoryWarning

viewDidUnload(可能由 1 引起)——显然只适用于 View Controller 类。

dealloc

didReceiveMemoryWarning

当系统内存不足时调用。

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

Action - 释放你不需要的任何东西,可能会撤销你在 viewDidLoad 中设置的东西。不要释放 UI 元素,因为这些应该由 viewDidUnload 释放。

问题 1 - 即使视图可见,它似乎也会被调用,因此很难看到您可以安全释放的内容。了解这一点以及可以发布的一些示例将非常有帮助。

viewDidUnload

当不可见的 View Controller 的 View 属性设置为 nil 时调用,手动或最常见的是通过 didReceiveMemoryWarning。

viewDidUnload 方法在那里,您可以: - 清理你想要的任何东西,以节省额外的内存或 - 如果您保留了一些 IBOutlets,以帮助释放在卸载视图时不会释放的内存。

Action - 通常你在 dealloc 中释放的任何 IBOutlets,也应该在这个方法中被释放(并且引用设置为 nil)。请注意,如果属性设置为保留,则将它们设置为 nil 也会释放它们。

dealloc

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

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

弹出视图控制器和内存

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

【问题讨论】:

关于问题2:你的意思是从导航控制器中释放视图还是弹出视图控制器? @Robin 0 弹出视图控制器。 【参考方案1】:

一些更正和建议:

didReceiveMemoryWarning实践

如您所说,控制器的默认实现didReceiveMemoryWarning 会在“安全”的情况下释放其视图。虽然从 Apple 的文档中不清楚“安全这样做”是什么意思,但通常认为它没有超级视图(因此视图当前无法可见),并且其 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,所以实际上没有要为控制器卸载的视图,viewDidUnload 将被调用,如果你调用didReceiveMemoryWarning的超类实现。

这就是为什么手动将视图控制器的 view 属性设置为 nil 不是一个好习惯的原因。如果这样做,您最好也发送viewDidUnload 消息。我想您对viewDidUnload 的理解更可取,但显然这不是当前的行为。

弹出视图控制器

如果您的意思是通过“弹出”来“从父视图中移除”,它确实会减少视图的保留计数,但不一定会释放它。

如果您的意思是从 UINavigationController 中弹出,它实际上会减少视图控制器本身的保留计数。如果视图控制器没有被另一个对象保留,它将被释放,最好是它的视图。正如我所解释的,viewDidUnload 这次将不会被调用。

其他...

从技术上讲,保留计数可能不会降至零。该对象更有可能在不事先将计数设置为零的情况下被释放。

为了确保视图控制器本身通常不会因为内存警告而被默认行为释放。

【讨论】:

@MHC。这太好了,对这个有一个坚定的理解真的很好,现在我现在如何区分隐藏的视图。相信这也会对其他人有所帮助。谢谢。 如果我在 didReceiveMemoryWarning 中释放/设置一些缓存数据为零,我是否也应该在 viewDidUnload 中这样做?后者总是跟随前者吗? 是的,viewDidUnload 总是跟在 didReceiveMemoryWarning 之后。 所以我不应该需要把它都装进去? 没有。如果您期望 viewDidUnload 将被调用,则在此处释放数据。同样,不幸的是,viewDidUnload 不能保证在视图被卸载时被调用。如果要处理视图可以被其他人显式卸载的情况,则必须在其他地方处理。【参考方案2】:

didReceiveMemoryWarning

...

Action - 释放你不需要的任何东西,可能会撤销你在 viewDidLoad 中设置的东西。

这是错误的。您在viewDidLoad 中重新创建的任何内容都应在viewDidUnload 中发布(并设置为nil)。正如您在下面提到的,didReceiveMemoryWarning 在视图可见时也会被调用。在didReceiveMemoryWarning 中,您应该释放诸如缓存或其他您所持有的视图控制器之类的东西,可以在下次需要它们时延迟重新创建(即,通过手动实现它们的 getter)。

viewDidUnload

...

Action - 通常你在 dealloc 中释放的任何 IBOutlets,也应该在这个方法中被释放(并且引用设置为 nil)。请注意,如果属性设置为保留,那么将它们设置为 nil 也会释放它们。

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

dealloc

...

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

正确。值得注意的是,这包括您在viewDidUnload 中处理的所有对象,因为后者在dealloc 进程中没有被隐式调用(AFAIK,不完全确定)。这就是为什么必须在viewDidUnload 中将所有发布对象设置为nil 的原因,因为否则您可能会发布两次(首先在viewDidUnload,然后再次在dealloc;如果您将指针设置为nil,则dealloc 中的释放调用将无效)。

弹出视图控制器和内存

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

不一定。这是您不应该关心的实现细节。无论当前的做法是什么,Apple 都可以在下一个版本中改变它。

【讨论】:

感谢您的澄清和更正。我现在可以放心地去清理我的代码了。【参考方案3】:

只是为了更新这个线程以使其与 ios6 相关:

viewDidUnload 和 viewWillUnload 在 iOS6 中已被弃用。这些方法永远不会被调用。

有关此方法和其他已弃用的方法,请参阅:http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/DeprecationAppendix/AppendixADeprecatedAPI.html

【讨论】:

【参考方案4】:

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

【讨论】:

以上是关于didReceiveMemoryWarning、viewDidUnload 和 dealloc的主要内容,如果未能解决你的问题,请参考以下文章

iOS 6 中的 didReceiveMemoryWarning

应该将 viewDidUnload 中的哪些工作移至 didReceiveMemoryWarning?

当应用程序在后台时没有收到 didReceiveMemoryWarning

Swift 惰性变量和 didReceiveMemoryWarning

如何在OpenGL应用程序中响应didReceiveMemoryWarning

如何实现 didReceiveMemoryWarning?