为什么在viewDidUnload中将nil分配给IBOutlets?

时间:2011-09-06 10:43:12

标签: iphone objective-c ios cocoa-touch uikit

我有一个UIViewController IBOutletUILabel,标签在XIB中连接。

#import <Foundation/Foundation.h>

@interface MyViewController : UIViewController {
    IBOutlet UILabel *aLabel; 
}
@end

根据iOS Programming: The Big Nerd Ranch Guide (2nd Edition)第7章

  

当[MyViewController]重新加载其视图时,将从XIB文件创建一个新的UILabel实例。

因此,它建议在viewDidUnload中发布标签。

- (void)viewDidUnload {
   [super viewDidUnload];
   [aLabel release];
   aLabel = nil;
}

作为一名C#程序员,我已经把它鼓起来,为事物分配nil / null是没有意义的。虽然我可以看到它在Objective-C中更有意义,但它仍然会略微提升我对代码美感的感觉*。我删除了它,一切正常。

但是,当我尝试加载NIB时尝试对MKMapView应用程序错误EXC_BAD_ACCESS执行类似的操作。

#import <Foundation/Foundation.h>

@interface MyViewController : UIViewController {
    IBOutlet MKMapView *mapView;
}
@end

- (void)viewDidUnload {
    [super viewDidUnload];
    [mapView release];
    mapView = nil; // Without this line an exception is thrown.
}

为什么mapView未设置为nil时出现错误,而aLabel未设置为nil时出现错误?

*我意识到我需要调整我对新语言的代码美感,但这需要时间。


事实证明,我对aLabel未被引用感到错误。不知道是什么让我觉得不是。

然而,这仍然留下了为什么在加载NIB时它们被引用的问题。

设置字段或属性后,会向旧值发送释放消息(合成属性集方法发送释放消息,或setValue:forKey:发送消息(如果是字段)。由于旧值已经发布,因此会生成EXC_BAD_ACCESS

2 个答案:

答案 0 :(得分:2)

当设备收到内存警告时,通常会调用

viewDidUnload

在View堆栈中,可能存在设备可以通过释放未使用的对象来释放内存的情况。想象一下,你有一个带有许多视图控制器的导航堆栈。堆栈中较低的接口视图控制器无法访问,但仍在使用内存。所以通常一个好主意是找出任何无法访问的界面元素。然后在需要时使用viewDidLoad重新加载它们。

通常在ViewDidUnload中,您应该释放从Nib文件创建或在ViewDidLoad方法中分配的任何视图对象

您的mapView正在抛出异常,因为您的视图控制器正在尝试访问MapView,但mapView已被释放。将插座设置为nil时,将忽略发送给它的任何消息。

答案 1 :(得分:2)

这是因为内存管理,特别是缺少垃圾收集。

在C#中(如您所知),不再包含不在范围内的对象。在objective-c中,这不会发生。完成后,您必须依赖保留/释放来告诉对象。

你的mapView bug展示的objective-c引用计数方法有一个缺点。在对象上调用release可能会导致它被释放。但是,你指向对象的指针仍然指向同一个地方 - 你的对象就不再存在了。

例如

// We create an object.
MyObject *object = [[MyObject alloc] init];

// At this point, `object` points to the memory location of a MyObject instance
// (the one we created earlier). We can output that if we want :
NSLog(@"0x%08X", (int)myObject);

// You should see a number appear in the console - that's the memory address that
// myObject points to.
// It should look something like 0x8f3e4f04

// What happens if we release myObject?
[myObject release];

// Now, myObject no longer exists - it's been deallocated and it's memory has been
// marked as free

// The myObject pointer doesn't know that it's gone - see :
NSLog(@"0x%08X", (int)myObject);

// This outputs the same number as before. However, if we call a method on myObject
// it will crash :
NSLog(@"%@", myObject);

在objective-c中,如果您尝试在nil上调用消息,则不会发生任何事情。因此,如果每次完成对象并在其上调用release时,您还应将其设置为nil - 这意味着如果您再次尝试使用该指针,它将不会崩溃!