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 released 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...
Related
I've been trough some tutorials and some information, but I dont have a straight answer about the best way or the best place to release variables.
Lets see this situation, I have these 2 variables:
#property (nonatomic, strong) IBOutlet UIButton *myButton;
#property (nonatomic, strong) NSString *myString;
...
#synthesize myButton = _myButton, myString = _myString;
Is this the best way of releasing them?:
-(void)viewDidUnload {
self.myButton = nil;
self.myString = nil;
[super viewDidUnload];
}
-(void)dealloc{
[_myButton release];
[_myString release];
[super dealloc];
}
I understand more than enough when dealloc is called, and when viewDidUnload is called, I just want to know if that way is correct, and why it has to be done in that way.
Thanks guys
It is recommended to not use property accessor methods in init and dealloc, since they might expect some other information in the object to exist. If you know what you are doing, there is really no problem of using self.myButton = nil also in dealloc. However, there is really no use of setting them to nil since the object is being destroyed - unless some code is accidently run from dealloc (as the accessor methods I mentioned above).
In viewDidUnload, you want to set your members to nil instead of only releasing them, since your object will continue to exist and does not want to access dangling pointers.
So, your example should be the best performance and safest way of programming.
There are other ways, such as not declaring the IBOutlets as properties, or declaring them as properties without retaining. I find that in most cases I prefer to have them be retained properties, which I then have to explicitly release. An example of this is when you switch from one view controller to another. As one view controller is dismissed, its views are removed and they are released. Any IBOutlet UILabels on that view would get released too if I don't have them retained. That means that when I flip back to the old view, I have to go through and reset my labels and controls to their last values, when I could have easily kept them saved if I just retain the IBOutlet.
I have read the documentation about the use of the viewDidUnload method. I thought I did understand the meaning of this method
1) I use retain with #propery in the .h file and #syntesize in the .m-file. I assign object with a value using self.object = something. In this case I would set self.object = nil; in the viewDidUnload method.
2) I do not retain the object in the .h-file but I am allocing and init the object in the viewDidLoad method. In the dealloc method I have the [object release]; In the viewDidUnload method I also set [object release]; because the next time the view will appear, the object will be created in the viewDidLoad method
Hope, this will help you...
I have a MBProgressHUD that I allocate as follows:
self.progressHUD_ = [[[MBProgressHUD alloc] initWithView:self.view] autorelease];
if I call removeFromSuperview then would I have to call progressHUD release again? Also if I declare a property with something like this:
NSString * title_;
#property (nonatomic, retain) NSString * title_;
then it is guaranteed that in my dealloc I should have a release on title right?
If progressHUD_ is a retain property, then you will have to release it in dealloc. However, the nice thing about a retain property is that you only have to set it to nil to reclaim the memory; making sure to use "self." before.
e.g.
self.<property_name> = nil;
// or in your case
self.progressHUD_ = nil;
// the following will not release it because it's not accessing the property:
progressHUD_ = nil
I do not recommend using [progressHUD_ release] because it can cause problems. e.g. if elsewhere you had released progressHUD_ and not set it to nil, you may accidentally release a pointer which is no longer allocated (dangling pointer).
I also recommend calling self.progressHUD_ = nil; in viewDidUnload which is called during low memory conditions and the view is not showing. It doesn't kill your class instance, but just unloads the view. And of course this assumes that you call self.progressHUD_ = [[[MBProgressHUD alloc] initWithView:self.view] autorelease]; in viewDidLoad rather than in init...
No, you don't have to release it again. Views retain their subviews and release them again automatically when you call removeFromSuperview. As long as the view has been autoreleased when you attach it to the view, it will be released when it is removed from the view.
I didn't quite understand your second question, but yes, you have to release any properties of type "retain" or "copy" in your dealloc statement. You have to write those release statements manually, they aren't added automatically (unless you are using ARC of course, which I strongly recommend).
How is your progressHUD_ property defined? (btw, the ivar should have a trailing underscore, but not the property name).
In case it is defined as (retain, whatever), you will have to release it again:
When you create it, its retainCount is +1.
When you assign it to your property, its retainCount will be increased by one.
When you add it as a subview to the parent view, its retainCount will be increased by one.
At some point, autorelease will eventually decrease it by 1, but the view and the property still hold on to it.
So you'll have to either set your property to nil or call release on the ivar in your dealloc method.
Also, you probably want to use copy instead of retain when defining an NSString property. And yes: you'll have to release it either way.
I have some doubt regarding retain in .h file. I know that if we alloc/copy/retain than we need to release it, but in following case
#property (nonatomic, retain) IBOutlet UITableView *myTable;
Do I need to release this table view object in my dealloc. I have created this tableview using xib.
Thanks.
So sayeth the docs:
Objects in the nib file are created with a retain count of 1 and then
autoreleased. As it rebuilds the object hierarchy, however, UIKit
reestablishes connections between the objects using the
setValue:forKey: method, which uses the available setter method or
retains the object by default if no setter method is available. If you
define outlets for nib-file objects, you should always define a setter
method (or declared property) for accessing that outlet. Setter
methods for outlets should retain their values, and setter methods for
outlets containing top-level objects must retain their values to
prevent them from being deallocated.
And:
When a low-memory warning occurs, the UIViewController class purges
its views if it knows it can reload or recreate them again later. If
this happens, it also calls the viewDidUnload method to give your code
a chance to relinquish ownership of any objects that are associated
with your view hierarchy, including objects loaded with the nib file,
objects created in your viewDidLoad method, and objects created lazily
at runtime and added to the view hierarchy. Typically, if your view
controller contains outlets (properties or raw variables that contain
the IBOutlet keyword), you should use the viewDidUnload method to
relinquish ownership of those outlets or any other view-related data
that you no longer need.
So basically, when being loaded from a NIB/XIB, the property is used. Meaning, if you specify retain properties on your IBOutlets (which you should), you need to release them. The preferred way to do this is in viewDidUnload, using the property.
#property (nonatomic, retain) IBOutlet UITableView *myTable;
...
- (void) viewDidUnload
{
self.myTable = nil;
}
Yes you will have to as you would be creating an object in the .h file and allocating it memory.. The only thing you are doing in XIB is creating a link between the two (XIB just acts as an outlet for the inner tableview) , but if you posted a button using the xib and did not link it via the code then you don't have to release it...
First of all you are not retaining anything in .h file.
The purpose of #property declaration in the .h file (it can also be don in .m file) is to tell the compiler how to handle the getters and setters for this property when you use (dot syntax).
Example:
Declaring property in the following way:
#property (nonatomic, retain) IBOutlet UITableView *myTable;
Tells the compiler that when you create a UITableView in your .m file like so:
- (id)initWithTable:(UITableView *)table
{
self = [super init];
if (self) {
self.myTable = table;
}
return self;
}
Compiler will automatically know to retain it, and so you would also need to release it.
But if you would declare your property in the following way:
#property (nonatomic, assign) IBOutlet UITableView *myTable;
and created the tableView as in the previous example
- (id)initWithTable:(UITableView *)table
{
self = [super init];
if (self) {
self.myTable = table;
}
return self;
}
The compiler would only assing the value of myTable to point to table. You would not own it and should not release it.
No u dont need to
u only need to release object which you have allocated.
since the table view is allocate in the xib it's release should be none of your concern
hope this helps
it should be release. if you want to see difference then run your application in instruments and check it.
I have some UIImageViews that are nested in UIViews, which group them together. They're owned by an view controller. As soon as the -viewWillDisappear method is called, I want to remove those UIImageViews with their UIViews alltogether, so that the memory gets freed up.
I call -release on the UIViews that contain the UIImageViews as subviews. Nothing else owns the UIImageViews. All I have is an instance variable to the UIView which groups the UIImageViews together.
So when I send -release to it, the retain count should become 0. But that's not a guarantee that I get more free memory, right? So, I also set myUIView = nil. Is that helpful / useful? How do you free up memory safely?
The best thing to do in -viewDidDisappear (not -viewWillDisappear; you're still onscreen at that point) for this case is to call self.view = nil. That will dump the entire view controller's view, which will be automatically reloaded for you next time it's needed.
If you have any IBOutlets on those views, you need to set them to nil, too or they won't release. Assuming you have a setup like this:
#interface MyViewController : UIViewController
{
UIImageView *_imageView;
}
#property (readwrite, retain) IBOutlet UIImageView *imageView;
#implementation MyViewController
#synthesize imageView = _imageView;
Then you need to call self.imageView = nil in your `-viewWillDisappear.
You generally should not call -release on your ivars except in their accessors and in -dealloc. The correct way to release them is to set them to nil using an accessor.
Caling -release when the retainCount is 1 will immediately call free(), which will recover memory. Remember, however, that other things may be retaining the object, including the autorelease pool, so you may not be certain what the retain count is at any given time.
I strongly advise people when they release a variable to immediately set it to nil. This doesn't impact the variable; it just protects you from chasing a pointer that you've released and so may now point to unallocated memory. So in -dealloc, I have lines that look like this:
[_stuff release]; _stuff = nil;
[_otherStuff release]; _otherStuff = nil;
I have a .xib file containing a UIView and 2 UILabel subviews linked to a class named Note with outlets assigned to each label appropriately, the definition for this class contains the following.
#interface Note : UIView {
IBOutlet UILabel *time;
IBOutlet UILabel *content;
}
I'm constructing this with the following code
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"Note" owner:self options:nil];
note = [nibViews lastObject];
[self addSubview:note];
Now, in my Note class dealloc phase, I'm not releasing either time or content, but I'm wondering if I should?
- (void)dealloc {
[super dealloc];
}
I'm assuming I don't because I'm not explicitly retaining these objects anywhere in my code, and I don't synthesize these into getter/setters. But I don't know enough about nib unarchiving to know whether I should be releasing these in my dealloc phase or not?
You should release an IBOutlet even if you're not writing or synthesizing accessor methods. The NIB lifecycle documentation says that although unarchived objects are initially set to autorelease, the retain count on them is bumped up by an extra 1 when UIKit hooks up all the IBOutlet connection bindings. You therefore need to manually decrement via release when you're done.
It's not obvious that UIKit would be doing this so you might assume you can just leave the setter/getter methods off and trust that everything is autoreleased. But that's not the case.
Notice that Interface Builder templates explicitly release any IBOutlets, so any you add should be treated likewise.
This is correct for the iPhone; it would not be correct on the Mac, though.
However, you may want to rework this code. It isn't safe to assume that the view will be the last object loaded from the nib. I'd suggest instead that you either connect it to a "note" outlet in your view controller or scan the list for an object that's a subclass of Note. (If you're loading multiple Notes and you use the outlet option, just make sure you add one Note before loading another.)
You DO need to release it. See
"As it rebuilds the object hierarchy, however, UIKit reestablishes connections between the objects using the setValue:forKey: method, which uses the available setter method or retains the object by default if no setter method is available." in Nib Object Retention
In other words, because you haven't specified these entries as #property (which implicitly declares a setter), or provided a setter directly, there is no available setter method, and the final part of this paragraph applies - the object is retained by default.
You should, in your dealloc() method, set all IBOutlets to nil using
self.outletName = nil;
If no setter is defined, then setValue will autorelease the old value (make sure you have an NSAutoreleasePool if running in a thread). If a setter is defined, it will perform whatever behaviour you defined. Either way, the set to nil will do exactly the right thing, and ensure you get no memory leaks. Do NOT do this
outletName = nil;
This will directly set the member variable, and bypass calling setValue.
See the documentation of NSObject setValue:forKey for more details.
Running the Performance Tool (Leaks) will not show a leak, but you can verify that there is actually a leak by looking at the current running total of allocated memory.
cf The Airsource - Memory Management and NIBs
You will not be able to take them out of memory unless you declare the IBOutlets as properties and synthesize them to ivars:
#interface Note : UIView {
IBOutlet UILabel *time;
IBOutlet UILabel *content;
}
#property (nonatomic, retain) IBOutlet UILabel *time;
#property (nonatomic, retain) IBOutlet UILabel *content;
#end
#implementation Note
#synthesize time, content;
// your methods
- (void)dealloc {
[time release], time = nil;
[content release], content = nil;
[super dealloc];
}
#end
I belive you are correct. See the documentation links below for more details.
Documentation:
The Nib Object Lifesycle
Dicussion Thread at Apple support boards
Another discussion thread