为了清晰和一致性,建议您声明所有 IBOutlet 的属性。
详细信息已在内存管理编程指南 http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmNibObjects.html。基本要点是,当您的 NIB 对象取消归档时,nib 加载代码将使用 setValue:forKey: 遍历并设置所有 IBOutlet。当您在属性上声明内存管理行为时,发生的事情并不神秘。如果视图被卸载,但您使用了声明为保留的属性,您仍然拥有对文本字段的有效引用。
也许一个更具体的例子有助于说明为什么应该使用保留属性:
我将对您正在工作的上下文做出一些假设 - 我假设上面的 UITextField 是由 UIViewController 控制的另一个视图的子视图。我假设在某个时刻,视图离开屏幕(可能它在 UINavigationController 的上下文中使用),并且在某个时刻您的应用程序会收到内存警告。
假设您的 UIViewController 子类需要访问其视图才能将其显示在屏幕上。
此时,nib 文件将被加载,并且每个 IBOutlet 属性将由 nib 加载代码使用 setValue:forKey: 设置。这里需要注意的重要事项是将设置为 UIViewController 的 view 属性的顶级视图(它将保留此顶级视图)和您的 UITextField,它也将被保留。如果只是简单地设置它,则 nib 加载代码将在其上保留它,否则该属性将保留它。 UITextField 也将是顶级 UIView 的子视图,因此它将有一个额外的保留,位于顶级视图的子视图数组中,因此此时文本字段已被保留两次。
此时,如果您想以编程方式切换文本字段,您可以这样做。使用该属性可以使内存管理在这里更加清晰;您只需使用新的自动释放文本字段设置该属性。如果您没有使用过该属性,则必须记住释放它,并可以选择保留新的属性。此时,谁拥有这个新文本字段有些模糊,因为内存管理语义不包含在 setter 中。
现在,假设将另一个视图控制器推送到 UINavigation Controller 的堆栈上,以便该视图不再位于前台。如果出现内存警告,则该离屏视图控制器的视图将被卸载。此时,顶层 UIView 的 view 属性将被清空,它将被释放和释放。
由于 UITextField 被设置为保留的属性,因此 UITextField 不会被释放,因为它的唯一保留是顶级视图的子视图数组的保留。
如果 UITextField 的实例变量没有通过属性设置,它也会存在,因为笔尖加载代码在设置实例变量时保留了它。
这里强调的一个有趣的一点是,由于 UITextField 是通过该属性另外保留的,因此您可能不希望保留它以防出现内存警告。因此,您应该在 -[UIViewController viewDidUnload] 方法中清空该属性。这将摆脱 UITextField 上的最终版本并按预期取消分配它。如果使用该属性,您必须记住显式释放它。虽然这两个操作在功能上是等效的,但意图不同。
如果您选择从视图中删除文本字段而不是换出文本字段,则您可能已经从视图层次结构中删除了它并将属性设置为 nil,或者释放了文本字段。虽然在这种情况下可以编写正确的程序,但在 viewDidUnload 方法中很容易犯过度释放文本字段的错误。过度释放对象是一个导致崩溃的错误;将已经为 nil 的属性再次设置为 nil 是不行的。
我的描述可能过于冗长,但我不想遗漏场景中的任何细节。当您遇到更复杂的情况时,只需遵循这些指南将有助于避免出现问题。
另外值得注意的是,桌面上的 Mac OS X 上的内存管理行为有所不同。在桌面上,设置不带setter的IBOutlet不会保留实例变量;但如果可用的话,再次使用设置器。