do I have to release IBOutlets in -(void)viewDidUnload? - iphone

They say:
// Release any retained subviews of
the main view. // e.g. self.myOutlet =
nil;
I've never seen that previously. so I wonder if they talk about nib outlets here?

Similar to how anything you allocate in init should be unallocted in dealloc.
If you alloc memory in viewDidLoad, you should release it in viewDidUnload.
The issue gets confusing when you bring nib files into the picture. If you are manually loading the nib file, you should manually unload it and set all the IBOutlets to nil.

You don't have to if there is a reason to keep it around, but assuming you need to access the entity point to via the outlet if the view has been torn down then yes, you should release. Otherwise your view controller will be asserting a retain against all of the IBOutlets from yoour nib even though the nib itself has been unloaded, which prevents them from being dealloced. Since these objects may have textures and such backing them, and the iPhone uses main ram for video that can add up to a lot of wasted memory very quickly.
Additionally, if viewDidLoad is called again then a new object will have been created and assigned to that outlet when the nib is reloaded, so if you use it anywhere else (set properties in other objects to the object pointed to by the IBOutlet) then your app might end up in an inconsistent state.

Related

Memory management when using IBOutlet/NIBs

My app is nearly finished and I have been Profiling it using Instruments. I'm checking out retain counts of various objects.
I have been careful to release any objects which I have called alloc on, and these don't seem to be leaking - so thats cool.
However, I have a view controller which has a UIPickerView in it. I set that up by dragging it onto my NIB in IB, defined the property using IBOutlet, synthesized it, and then hooked it all up.
Every time I launch the view, it seems the number of UIPickerViews increases by one. I was under the assumption that I do not need to release this kind of thing myself, as I had assigned it to a property (using nonatomic, retain).
This is happening to all my UI stuff - buttons etc, not just the picker view. I was just using that as an example.
Can anyone help me out here?
Thanks!
When the view is loaded all the items created from the NIB are retained for you and your IBOutlet pointers are initialized pointing to those retained subview objects. You need to release those in viewDidUnload. Are you doing that?
In viewDidUnload you should release all objects created from the NIB and set those pointer to nil. You should also release those same object in dealloc. Example here.

How to properly compose a "viewDidLoad" method

I'm not entirely new to iPhone dev, but I ran across a situation where I was unsure about the best design choice for my code.
I have a view controller which asynchronously fetches an image from the internet and loads it into an image view. When the app receives a memory warning however, the imageView is released by didReceiveMemoryWarning. If the app receives a memory warning while loading an image, the imageView is nil by the time my code tries to load the image into the imageView.
What is the best practice for viewDidLoad? Should I just instantiate any variables that I may need later on? Or should I just check for nil values elsewhere in my code?
Also, in viewDidUnload, should I just set IBOutlet variables to nil? Why is this?
Thanks! -Matt
Why not just create the image view on demand when the image is loaded? No need to create it any earlier. Or, if you do, then just have code to recreate it if it has been released.
In viewdidunload you should release resources that you created in viewdidload (or load view). Including any IBoutlets that interface builder hooked up for you. It's good practice to set these variables to nil to ensure they won't be inadvertently accessed after release, or double released.
IBOutlets can be both instance variables and properties. In both case, we should release the references when the view, which is top most superview, is unloaded. Setting the IBOutlet retained properties to nil releases the reference automatically, but for instance variables we need to release them. We still need to set instance variables to nil because viewDidUnload is not dealloc, so we must comply with the managed memory rule or we might have invalid references sitting around inside the instance.
I don't think there are rules about what you should do in viewDidLoad, but if you did release and set some variables to nil in viewDidUnload, you need not have to check for nil again in the viewDidLoad unless you don't trust the framework.

memory management with outlets and properties in view controllers

Hey guys, sorry for beating the memory management (dead)horse again. I know this question has been asked several times on SO, but I feel all the threads out there is still lacking two pieces of information. Let me put forth what I DO know to save everyone some time.
1) When you create an IBOutlet, your view controller automatically retains this outlet.
1a) When you don't create an outlet, the 'framework'(the nib?) releases your objects(like uilabels or uiviews) for you.
2) When you do self.myOutlet = nil, you effectively release an outlet(providing that you have synthesized your properties correctly). This is because the setter releases the outlet and assigns it to nil.
What I don't know:
1) The MAIN question: If you do self.myOutlet = nil in viewDidUnLoad, do you still need to do anything in dealloc? Can you make the assumption that viewDidUnload always called before dealloc?(and therefore your retained views are released?)
2) If you don't synthesize a property for that outlet, what happens? Shouldn't the framework release it automatically?(since you don't have a retain property) If you do have to release it, how do you do it and where(in viewDidUnload or dealloc)?
If anything is wrong, please point it out to me. Any clarifications at all would be very helpful.
(#1) The Apple docs say to do both
In addition, because of a detail of the implementation of dealloc in UIViewController, you should also set outlet variables to nil in dealloc:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmNibObjects.html
(#2) If you don't synthesize a property, you still need to make a property, and it better retain -- read the link. The UI object is created, autoreleased and the outlet property is set (which is supposed to retain). There is no release called for you because they already did the release they were supposed to do -- you retained, so you have to release (in both viewDidUnload and dealloc)

What exactly must I do in viewDidUnload?

I tend to release my stuff in -dealloc, and now iPhone OS 3.0 introduced this funny -viewDidUnload method, where they say:
// Release any retained subviews of
the main view. // e.g. self.myOutlet
= nil;
So -viewDidUnload seems to get called when the view of the view controller has been kicked off from memory. And if I have subviews attached to the main view of the view controller, I have to release that stuff only HERE, but not in -dealloc as well?
That's confusing. Also, what if -dealloc causes the view to be unloaded (released)? Then again, it will call -viewDidUnload?
I do realize the difference, that -viewDidUnload is just for the case where the view itself gets killed, but the view controller stays in memory. And -dealloc is for the case where the whole thing goes to trash.
Maybe someone can clear up the confusion.
The intent here is to "balance out" your subview management. Anything that you create in viewDidLoad should be released in viewDidUnload. This makes it easier to keep track of what should be released where. In most cases, your dealloc method is a mirror-image of your init method, and your viewDidUnload will be a mirror image of your viewDidLoad method.
As you pointed out, the viewDid... methods are to be used when the view itself is loaded and unloaded. This permits a usage pattern in which the view controller remains loaded in memory, but the view itself can be loaded and unloaded as required:
init
viewDidLoad
viewDidUnload
viewDidLoad
viewDidUnload
...
dealloc
Of course, it doesn't hurt to release things in your dealloc method as well, as long as you set them to nil when you release them in viewDidUnload.
The following quote from the Memory Management section of Apple's UIViewController documentation, describes it in more detail:
...in iPhone OS 3.0 and later, the viewDidUnload method may be a more appropriate place for most needs.
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.
As you say viewDidUnload will be called if self.view=nil, this generally occurs if you get memory warning. In this method you must release any subview of the mainview which can easily be created by .xib or loadView method. You should release any data object if you create them in viewDidload or loadView etc. because these methods will be called again to present view to the user, those data can be recreated easily.
When you get a memory warning usually the viewcontroller will unload it's view but itself will not be dealloc.
All that can be re-created easily should be unloaded, but not the model of the view.

When should I release objects in -(void)viewDidUnload rather than in -dealloc?

What is the -(void)viewDidUnload is good for?
Could I not just relase everything in -dealloc? If the view did unload, wouldn't -dealloc be called anyway?
In addition to what has already been indicated, I wanted to elaborate more about logic behind -viewDidUnload.
One of the most important reasons for implementing it is that UIViewController subclasses commonly also contain owning references to various subviews in the view hierarchy. These properties could have been set through IBOutlets when loading from a nib, or programmatically inside -loadView, for instance.
The additional ownership of subviews by the UIViewController means that even when its view is removed from the view hierarchy and released to save memory, through which the subviews are also released by the view, they will not actually be deallocated because the UIViewController itself still contains its own outstanding retaining references to those objects as well. Releasing the UIViewController additional ownership of these objects ensures they will be deallocated as well to free memory.
The objects that you release here are usually recreated and set again when the UIViewController view is re-loaded, either from a Nib or through an implementation of -loadView.
Also note that the UIViewController view property is nil by the time this method is called.
As the documentation says:
It is called during low-memory conditions when the view controller needs to release its view and any objects associated with that view to free up memory.
In the same situation dealloc is not called. This method is only available in OS3 and above. Dealing with the same situation in iPhone OS 2.x was a real pain!
Update July 2015: It should be noted that viewDidUnload was deprecated in iOS 6 because "Views are no longer purged under low-memory conditions and so this method is never called." So, the modern advice is not to worry about it and use dealloc.
This is because you will typically set the #property as "(nonatomic, retain)" and as such the setter that is created for you releases the current object and then retains the argument i.e.
self.property = nil;
...does something along the lines of:
[property release];
property = [nil retain];
Therefore you are killing two birds with one stone: memory management (releasing the existing object) and assigning the pointer to nil (since sending any message to a nil pointer will return nil).
Hope that helps.
Remember that viewDidUnload is a method in the view controller, not in the view. The view's dealloc method will get called when the view unloads, but the view controller's dealloc method may not be called until later.
If you get a low memory warning and your view isn't showing, which will happen for instance about any time you use a UIImagePickerController to let the user take a picture, your view will get unloaded and will need to get reloaded after that.
Conclusion:
View Controllers have a view property. Typically a nib or piece of code adds other views to this view. This happens often inside a -viewDidLoad method, like this:
- (void)viewDidLoad {
[super viewDidLoad];
[self createManyViewsAndAddThemToSelfDotView];
}
in addition, a nib file may create a button and append it to the view controller's view.
On iPhone OS 2.2, when -didReceiveMemoryWarning was invoked from the system, you had to release something to free up memory. You could release the whole view controller's view if that made sense. Or just big memory-consuming contents in it.
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
Now, in the new OS 3.0, there is an -viewDidUnload method, which will be invoked from the system when the view has been unloaded because of low memory (please correct me: when exactly does this get called?)
-viewDidUnload is used to release all objects that were owned both by the view controller itself and the view. The reason: If a view controller holds references to childs of the view, i.e. a button, the referenced child views will not get released, because their retain count is >= 1. After they are released in -viewDidUnload, they can get freed up from memory.
Apple deprecated viewWillUnload, now you shoud use didReceiveMemoryWarning or dealloc to release your objetcs.
In iOS 6, the viewWillUnload and viewDidUnload methods of
UIViewController are now deprecated. If you were using these methods
to release data, use the didReceiveMemoryWarning method instead. You
can also use this method to release references to the view
controller’s view if it is not being used. You would need to test that
the view is not in a window before doing this.
If the view controller is popped from the navigation controller stack and is not retained anywhere else, it will be deallocated, and dealloc will be called instead of viewDidUnload. You should release the views created in loadView in dealloc, but it is not necessary to set the variables to nil, because soon after dealloc is called the variables will no longer exist.
You can release any subviews you hold on to, for example that UIImageView you retained in your loadView method, or better yet the image that was on that UIImageView.