开发者

Why assign nil to IBOutlets in viewDidUnload?

开发者 https://www.devze.com 2023-04-03 11:48 出处:网络
I have a UIViewController that has an IBOutlet for a UILabel with the label wired up in the XIB. #import <Foundation/Foundation.h>

I have a UIViewController that has an IBOutlet for a UILabel with the label wired up in the XIB.

#import <Foundation/Foundation.h>

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

According to iOS Programming: The Big Nerd Ranch Guide (2nd Edition) chapter 7

When [MyViewController] reloads its view, a new UILabel instance is created from the XIB file.

Thus it recommends releasing the label in viewDidUnload.

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

Being a C# programmer, I've had it drummed into me that assigning nil/null to things is pointless. While I can see it makes more sense in Objective-C, it still grates my sense of code aesthetic slightly*. I removed it and it all worked fine.

However, when I tried to do a similar thing with MKMapView the application errors EXC_BAD_ACCESS while trying to load the NIB.

#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.
}

Why is there an error when mapView is not set to nil, but not when aLabel is not set to nil?

* I realise I need to adjust my sense of code aesthetic for the new language, but it takes time.


It turns out I was just flat out wrong about aLabel not being referenced. Not sure what made me think it wasn't.

However, that still leaves the question of why they're being referenced while the NIB is being loaded.

When the field or property is set, a release message is sent to the old value (either the synthesized properties set method sends the release message, or setValue:forKey: sends the message if it's a field). Because the old value has already been relea开发者_StackOverflowsed, this results in EXC_BAD_ACCESS.


It's because of memory management, specifically the lack of garbage collection.

In C# (as you know) objects that are no longer in scope are removed. In objective-c, that doesn't happen. You have to rely on retain/release to tell the object when you are done with it.

There's a drawback to the objective-c reference counting method that your mapView bug exhibits. Calling release on an object might result in it being deallocated. However, your pointer to the object will still point to the same place - your object just won't be there anymore.

For example

// 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);

In objective-c, if you try to call a message on nil, nothing happens. So if each time you are finished with an object and call release on it, you should also set it to nil - this means that if you try to use that pointer again, it won't crash!


viewDidUnload is usually called when the device receives a memory warning.

In the View stack there may be situations where the device can free up memory by releasing objects that aren't in use. Imagine you have a navigation stack with a number of view controllers. The interface view controllers lower on the stack are not accessible but are still using up memory. So its usually a good idea to nil out any interface elements that can't be accessed. These will then be reloaded with viewDidLoad when needed.

Generally in ViewDidUnload you should release any view objects that are created from a Nib file or allocated in your ViewDidLoad method

Your mapView is throwing an exception because your view controller is trying to access to MapView but the mapView has been released. When you set the outlet to nil any messages sent to it are ignored.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号