Nib 对象的内存管理

转自http://www.apple.com.cn/developer/iphone/library/documentation/UserExperience/Conceptual/MemoryMgmt/Articles/mmNibObjects.html

Nib 对象的内存管理

在Cocoa应用程序运行时生命周期中,会有一个或多个nib文件被加载,而且它们所包含的对象会被解压。当不再需要它们时,谁来负责释放这些对象取决于您在哪种平台进行开发,而且,如果是在Mac OS X的话,还取决于您的File’s Owner继承自哪个类。

关于nib文件及其内存管理语义的基本讨论,以及与nib相关的术语的定义,比如插座对象,File’s Owner和顶层对象,请参考资源编程指南中的“Nib文件”一节。

插座对象

当一个nib文件被加载且插座对象被建立的时候,如果存取方法(在Mac OS X和iOS上都)存在,则nib加载机制一定会使用存取方法。因此,无论您在哪个平台上进行开发,一般情况下,您都应该使用Objective-C的属性声明功能声明插座对象。

声明插座对象的一般形式应该是:

@property (attributes) IBOutlet UserInterfaceElementClass *anOutlet;

插座对象的行为取决于平台的类型(请参考“Mac OS X”“iOS”),因此,实际的声明会有所不同:

  • 对于Mac OS X,您应该使用:

    @property (assign) IBOutlet UserInterfaceElementClass *anOutlet;
  • 对于iOS,您应该使用:

    @property (nonatomic, retain) IBOutlet UIUserInterfaceElementClass *anOutlet;

接下来,您应该做的是合成相应的存取方法,或者是根据声明实现这些方法,(在iOS平台)还要在dealloc中释放相应的变量。

如果您使用现代运行时并合成实例变量,这种模式同样适用,因此它能够在所有情况下保持一致性。

Mac OS X

在默认情况下,nib文件的File’s Owner负责释放nib文件中的顶层对象和nib中的对象创建的所有非对象资源。对象图中的根对象的释放会促使所有依赖它的对象也被释放。对应用程序的主nib文件(其中包含应用程序菜单和其他可能的项目)来说,它的“文件所有者”是全局应用程序对象NSApp。然而,当Cocoa应用程序终止时,主nib中的顶层对象不会因为NSApp被回收(请参考“回收对象”)而自动获得dealloc消息。换句话说,即使是在主nib文件中,您也必须管理顶层对象的内存。

应用程序套件提供了一系列功能来帮助您确保nib对象能够被正确地释放:

  • NSWindow对象(包括面板)有一项isReleasedWhenClosed属性,如果该属性被设为YES,则窗口会在关闭的时候释放它自己(和它的视图层次中所有相关的对象)。在Interface Builder中,您可以通过勾选检视器的属性面板上的“在关闭时释放”复选框来设置此选项。

  • 如果nib文件的“文件所有者”是一个NSWindowController对象(它是基于文档的应用程序中的文档默认nib—前面介绍过,NSDocument负责管理NSWindowController实例),它会自动销毁它所管理的窗口。

因此,在一般情况下,您负责释放nib文件中的顶层对象。但实际上,如果您的nib文件的所有者是一个NSWindowController实例,则它会为您释放顶层对象。如果您的某个对象加载了nib本身(而且它的所有者不是NSWindowController实例),则您可以为每一个顶层对象定义插座对象,以便您可以在适当的时候使用这些引用释放它们。如果您不希望为所有的顶层对象定义插座对象,那么您可以使用NSNib类的instantiateNibWithOwner:topLevelObjects:方法来获得一个包含nib文件顶层对象的数组。

当您考虑到应用程序的各种类型时,销毁nib对象的责任问题就变得更清晰了。大多数Cocoa应用程序归属于两类:单一窗口应用程序和基于文档的应用程序。在这两种情况下,系统会在一定程度上为您自动处理nib对象的内存管理。在单一窗口应用程序中,主nib文件中的对象在应用程序的整个运行周期内持续存在,并在应用程序终止时被释放;但是,系统并不保证在应用程序终止时自动地对主nib文件中的对象调用dealloc。在基于文档的应用程序中,每一个文档窗口都由一个NSWindowController对象管理,该对象处理文档nib文件的内存管理。

有些应用程序可能具有更复杂的nib文件和顶层对象的分布。例如,应用程序可能有多个nib文件,并带有多个窗口控制器,可加载的面板和检视器。但是,在大多数情况下,如果您使用NSWindowController对象来管理窗口和面板,或者如果您设置了“在关闭时释放”这一窗口属性,那么系统会为您自动处理大部分的内存管理。如果您决定不使用窗口控制器,也不想设置“在关闭时释放”属性,则您应该在窗口关闭时显式地释放您的nib文件的窗口和其他顶层对象。此外,如果您的应用程序使用检视器面板,那么(在延迟加载之后)该面板通常应该在整个应用程序生命周期内持续存在—没有必要销毁检视器和它的资源。

iOS

顶层对象

当nib文件中的对象被创建时,它们的保留计数为1,而且随后它们会被自动释放。由于重建了对象层次结构,UIKit会使用setValue:forKey:重新建立对象之间的连接,setValue:forKey:使用现有的setter方法,如果没有setter方法可用,那么它会默认保留这个对象。这就意味着(假设您遵循“插座对象”中所示的模式)具有插座对象的任何对象都保持有效。但是,如果存在您没有存储在插座对象中的顶层对象,则您必须保留loadNibNamed:owner:options:方法返回的数组或数组中的对象,以防止这些对象过早地被释放。.

内存警告

当视图控制器收到内存警告(didReceiveMemoryWarning)的时候,它应该释放那些当前不需要的,和那些如果有需要可以稍后重新创建的资源的所有权。其中一种资源就是视图控制器的视图本身。如果它没有父视图,则该视图应该被销毁(在它的didReceiveMemoryWarning实现中,UIViewController调用[self setView:nil])。

但是,由于nib文件中的元素的插座对象通常会被保留(见“插座对象”),因此,即使主视图被销毁,如果没有采取任何进一步的行动,插座对象也不会被销毁。这并不是其本身的问题—如果当主视图被重新载入时,它们只是被简单地替换掉—但这确实意味着,didReceiveMemoryWarning的效果被削弱了。为了确保您正确地释放插座对象的所有权,在您的自定义视图控制器类中,您可以实现viewDidUnload来调用您的存取方法,以便将插座对象设置为nil

- (void)viewDidUnload {
    self.anOutlet = nil;
    [super viewDidUnload];
}

注意:在iOS 3.0版本之前,viewDidUnload方法是不可用的。相反,您应该在setView:中将插座对象设置为nil,正如该例所示:

- (void)setView:(UIView *)aView {
    if (!aView) { // View is being set to nil.
        // Set outlets to nil, e.g.
        self.anOutlet = nil;
    }
    // Invoke super's implementation last.
    [super setView:aView];
}
此外,由于 UIViewController中的 dealloc的一个实现细节,您还应该在 dealloc中将插座对象变量设置为 nil

- (void)dealloc {
    // Release outlets and set outlet variables to nil.
    [anOutlet release], anOutlet = nil;
    [super dealloc];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值