推荐您为所有IBOutlets声明属性,以确保清晰和一致性。
具体细节可参考
Memory Management Programming Guide。基本意思是,在未归档NIB对象时,nib加载代码将使用setValue:forKey:设置所有IBOutlets。当您在属性上声明内存管理行为时,就不会有任何疑惑。如果该视图被卸载,但您使用了被声明为retain的属性,则仍然可以引用到您的textfield。
也许一个更具体的例子会更有用,以说明为什么应该使用保留属性:
我假设您的上下文环境是这样的——上面提到的UITextField是另一个由UIViewController控制的视图的子视图。我假设在某个时刻,该视图已经离开了屏幕(也许它是在UINavigationController的上下文中使用),并且在某个时候,您的应用程序会收到内存警告。
因此,假设您的UIViewController子类需要访问其视图以在屏幕上显示它。此时,nib文件将被加载,并且每个IBOutlet属性都将使用setValue:forKey:由nib加载代码设置。这里需要注意的重要部分包括将设置为UIViewController的view属性的顶级视图(这将保留此顶级视图)以及您的UITextField,它也将被保留。如果它只是被设置,nib加载代码会对其进行保留,否则该属性会将其保留。UITextField还将是顶级UIView的子视图,因此它将受到额外的保留,成为顶级view的子视图数组中的一部分,所以此时textfield已经被保留了两次。
如果你想要以编程方式切换文本字段,那么现在你可以这样做。使用属性可以更清晰地管理内存;你只需用一个新的自动释放的文本字段设置该属性即可。如果没有使用该属性,你必须记住释放它,并可能保留新的文本字段。此时,由于内存管理语义不包含在setter中,所以新的文本字段属于谁有些模糊。
现在假设另一个视图控制器被推到了UINavigation Controller的堆栈上,因此该视图不再处于前台。在发生内存警告的情况下,这个离屏视图控制器的视图将被卸载。此时,顶级UIView的视图属性将被置空,它将被释放并取消分配。
由于UITextField被设置为被保留的属性,因此UITextField不会被释放,就像它属于顶级视图的子视图数组一样被释放。
如果没有通过属性设置UITextField的实例变量,则它也会存在,因为当设置实例变量时,nib加载代码已经保留了它。
这个例子强调的一个有趣点是,由于UITextField通过属性另外被保留,你可能不希望在内存警告时保留它。因此,你应该在- [UIViewController viewDidUnload]方法中将该属性置为nil。这将消除UITextField上的最终释放并按预期释放它。如果使用属性,则必须明确释放它。虽然这两个操作在功能上是等效的,但意图不同。
如果你选择从视图中删除文本字段而不是交换出文本字段,你可能已经从视图层次结构中删除了它并将其属性设置为nil,或者释放了该文本字段。虽然在这种情况下编写正确的程序是可能的,但很容易犯错误,在viewDidUnload方法中过度释放文本字段。过度释放对象是一个导致崩溃的错误;再次将已经为空的属性设置为nil则不会。
我的描述可能过于冗长,但我不想在情境中漏掉任何细节。遵循指南将有助于您在遇到更复杂的情况时避免问题。
此外值得注意的是,在Mac OS X桌面版本上,内存管理行为有所不同。在桌面版本上,如果没有setter方法,设置IBOutlet不会保留实例变量;但如果setter方法可用,仍然会使用它。