iOS - What is best way to manage memory for IBOutlets?
I've been reviewing the Apple docs and sample code to try to determine the best way to manage memory for IBOutlets. I'm a little confused, to say the least.
The CurrentAddress sample code declares IBOutlets as properties:
@interface MapViewController : UIViewController <MKMapViewDelegate, MKReverseGeocoderDelegate>
{
MKMapView *mapView;
UIBarButtonItem *getAddressButton;
}
@property (nonatomic, retain) IBOutlet MKMapView *mapView;
@property (nonatomic, retain) IBOutlet UIBarButtonItem *getAddressButton;
Great. And these are release开发者_运维技巧d in dealloc:
- (void)dealloc
{
[mapView release];
[getAddressButton release];
[super dealloc];
}
Now shouldn't these properties be set to assign? Because when set to retain, the IBOutlet's retain count will be increased twice: once when the nib is loaded and another time when the property is set? And wouldn't it be better to set these properties to nil instead of releasing in dealloc?
Apple docs says we should retain properties for iOS.
Retained outlets should be released and nil
'ed in both dealloc
and viewDidUnload
.
On the Mac, every outlet which is not retained by a superview is automatically retained when loading the nib. That's not the case with iOS. That's why it's theoretically valid to only retain outlets other than views in the view hierarchy.
There's a very helpful post by Jeff LaMarche regarding this topic: Outlets, Cocoa vs. Cocoa Touch.
Once the nib loader finishes loading everything and connecting all the IBOutlets, it autoreleases all the objects it loaded. If your IBOutlet property was declared as assign
, then the object it points to would be deleted next time the autorelease pool emptied.
You can set the properties to nil in dealloc instead of directly releasing them, the result is the same. The thing to watch for is, if you've provided your own implementation of the setter, you need to keep in mind that some of the other members of your object may already have been released.
This is different for MacOSX and iOS. In iOS the retain count will be two after the view is loaded and the nib connections are established.
Each of these elements will be retained once by the view and once by your controller. Additional elements in the view will be retained only by the view only.
When your controller releases the two elements, their retain count goes down to one. After that [super dealloc] is called. UIViewController has a [view release] in its dealloc, so the view is released (unless retained elsewhere, or previously released). When the view is deallocated, it releases its sub views, and the elements are finally completely freed.
The reason why [object release] is preferred in dealloc, is that key-value coding (or your own code) might cause additional code to be run when you write [self setObject:nil]. This can potentially cause other objects to interact with your controller when it is in the middle of deallocating itself. Setters should not be used in the init method for the same reason.
There is a second reason for just doing release. By leaving the value and not setting it to nil, we'll notice if code erroneously access that variable on our object later during dealloc. This can help catch bugs that might not be easy to track down otherwise.
I'm assuming you @synthesize
these properties. If you didn't, you would need to release yourself manually. You are very correct in your assumption that if you continued to retain when a property is set, you would leak memory.
Let's think.... what did properties used to look like before we had the fancy @synthesize
statement?
id _propertyName; // the ivar
- (id) propertyName {
return _propertyName;
}
- (void) setPropertyName:(id)v {
if (_propertyName) {
[_propertyName release]; // release the previously retained property
}
_propertyName = [v retain]; // retain this one so it doesn't fly away on us
}
Now, you don't need to type this stuff, because @synthesize is cool and generates that for you, it will also generate @synchronized
blocks if you do not specify something as being nonatomic
, which is also pretty rad.
If you specified assign
instead of retain
, you'd get something like this
id _propertyName; // the ivar
- (id) propertyName {
return _propertyName;
}
- (void) setPropertyName:(id)v {
_propertyName = v;
}
This is about the only thing you CAN do when things are not objects, because they are only values (also sometimes referred to as value types, objects are reference types). Since value types cannot be retained, the other type of block wouldn't make any sense. Go ahead and try to create a retain property with BOOL and watch what LLVM or GCC tell you to go do with what ;)
shouldn't these properties be set to assign? Because when set to retain, the IBOutlet's retain count will be increased twice: once when the nib is loaded and another time when the property is set
well, the code you posted is right as it is.
when you use:
@property (nonatomic, retain) IBOutlet MKMapView *mapView;
you are just telling xCode to create a setter method which will create your MKMapView object and retain it everytime you call
yourMapViewController.mapView = someMapView; // from out
// or
self.mapView = someMapView; // from in
after that mapView retain count increase +1 and your MapViewController code need that 'couse now you can point to mapView and manage it...
don't worry for the IB nib file...
when you load a UIViewController with a nib, in your case the class MapViewController : UIViewController, the IB nib objects will release when you release your MapViewController... just care of the objectS you retain...
精彩评论