I have a doubt that how we will release the instance variables properly.I know we can do the release in the dealloc method. If we are given as self.instancevariouble=nil in the viewdidunload also will release the object. My question is If there is any problem if we are given the object as nil in the unload method and then also release it in the dealloc method. Also how do I need to release a tableview which is added as IBoutlet in the xib.Can anybody point me in the right direction?
There is no problem with calling release on a nil object but, as a better strategy, move to ARC and most of these concerns will just go away. (Also note that iOS 6 does not unload views.)
If you try to release a nil object it will "work". You can send a message to a nil object without the app crash ;) so you can do this :
UILabel *lbl = nil;
[lbl release];
It won't do anything.
And about "How i need to release a tableview which is added as IBoutlet in the xib.Can anybody point me in the right direction?" You have to "connect" your UITableView in the xib file with your code and do a :
[myTableView release];
If you use self.instanceVariable = nil in the viewDidUnload method (which by the way does no longer get called in iOS 6) the value is of course nil. So if you then release that in the dealloc method you basically call [nil release]; and that does nothing.
Related
I know memory management in iOS is tricky subject to newbies like me, but I was hoping for a clear explanation here on stackoverflow which I could not find anywhere else.
So, pretend I have a property / ivar
#property(nonatomic, retain) UIPopoverController *popOver;
which I'm allocating like this:
self.popOver = [[[UIPopoverController alloc] initWithContentViewController:popOverContent] autorelease];
Now, in my dealloc and viewDidUnload methods, I do both
// in viewDidUnload:
self.popOver = nil;
// in dealloc:
[popOver release];
Question:
If I do nil / release in viewDidUnload / dealloc, do I really need to autorelease at allocation?
Vice versa, if I do autorelease at allocation, do I need to nil / release later?
What's the difference, if any?
Thanks in advance for your time - I'll continue reading, seriously memory management can't be that hard to wrap your head around...
Don't be confused by the autorelease in this line:
self.popOver = [[[UIPopoverController alloc] initWithContentViewController:popOverContent] autorelease];
After this statement you effectively own the object because the property setter claimed ownership of it. The autorelease balances the alloc-init.
So... yes, you need to autorelease at allocation. If you did this (no autorelease), you would leak:
self.popOver = [[UIPopoverController alloc] initWithContentViewController:popOverContent];
Another option is to use a temporary variable instead of autorelease:
UIPopoverController *temp = [[UIPopoverController alloc] initWithContentViewController:popOverContent];
self.popOver = temp;
[temp release];
Either way you need to release the object in dealloc.
1 If I do nil / release in viewDidUnload / dealloc, do I really need to autorelease at allocation?
Yes.
2 Vice versa, if I do autorelease at allocation, do I need to nil / release later?
Yes.
In the first case, the auto-releasing is done on behalf of that method. That method doesn't need the popover anymore, so it needs to (auto)release it.
At dealloc, your object doesn't need the popover anymore. Therefore, you need to release it.
It's very simple. You don't have to consider long-term object ownership; you just need to think very locally, at the level of every method. The decision of releasing it or not doesn't at all depend on whether that object is kept by some other parts of the program. In a method, if you alloc an object and you no longer need it in that method, you (auto)release it.
The dealloc is a slight exception to the rule. There, you need to release the ownership of all the instance variables.
That's it!
Yes. But maybe not in this case.
Yes. But maybe not in this case.
viewDidUnload is called when the the view is unloaded, dealloc is called when the view Controller is being destroyed.
In viewDidUnload you release objects that are used by the view that are not needed anymore and can be recreated in viewDidLoad. Obvious, since the view is not being displayed it doesn't need to hold on to the objects that are set up by the view controller for it.
In dealloc you are cleaning up the viewController and here you clear up all it's resources, including those that it has assigned to the view.
In this case, the view does not own the popover controller - it should be owned by the view controller, so there is no need to release it in viewDidUnload but you do need to release it in the dealloc.
I have an ivar that is a UIPopoverController. When it gets dismissed, I want to clean up memory. I call the following method when the popover gets dismissed, or if the user pressed the BarButtonItem when the popover is already being presented to the user.
- (void)popoverCleanup:(UIPopoverController *)popoverController {
popoverController.delegate = nil;
popoverController = nil;
}
I set a breakpoint and tried following the code and looking at the memory. I see that my ivar Popover and the method parameter popoverController both point to the same memory address. When I hit the first line, I see the delegate set to nil on both the Popover and popoverController parameter. When I reach the end of the method however, I see only popoverController's memory set to nil, whereas the ivar Popover does not get set to nil. Is there a reason for this?
Thanks.
By doing this, you are just saying you don't want to hold a popoverController pointer, no matter to what it points. Nothing get's released here. Proper way to release a popover is implementing it's delegate method for dismissing and release it there.
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
[popoverController release];
}
Keep in mind that setting an ivar to nil doesn't release the object that ivar was pointing to, it usually leaks it. You can only do that with copied or retained properties because they work so that the retain or copy the new value (nil, which does nothing when retained or copied as it's not really an object) and release the old one. You should only set the ivar to nil after releasing it so that if you accidentally send it a message later, you don't crash for accessing released memory.
Edit: also keep in mind that there is really no need for ivar if you only want to present a popover and release it. Just create a local variable and present a popover. Once delegate method gets called it will have a pointer to the popover object, no need to keep ivar just for that. Only case where you would need an ivar for popover is if you wanted to create it and present it multiple times, then release it when done with it forever, like when leaving view controller from which you presented it.
This is because popoverController and your instance variable are not the same variable even though they point to the same thing (same pointer instance before you set it to nil).
In that method (- (void)popoverCleanup:(UIPopoverController *)popoverController) you need to set your instance variable to nil rather than popoverController variable. Also if you are not already, you'd need to release your popover and it should set the instance variable to nil if there is nothing else retaining it
It should be noted that setting an instance variable to nil does not release the object pointed to.
If you want to release the object, the simplest thing is to have it managed as a property and set the property to nil.
Bear in mind that popoverController = nil; is not the same as self.popoverController = nil;. The former is setting the INSTANCE VARIABLE to nil. The latter is setting the PROPERTY to nil, and if the property is defined with "retain", the the addressed object will be released.
I have a general question regarding memory management of properties. Currently, I always use properties without any explicit declaration of related ivars. And, for every retained or copied property I'm releasing its retain count both in dealloc and viewDidUnload methods:
-(void)dealloc{
[self.myProperty release];
[self.myOutlet release];
[super dealloc];
}
- (void)viewDidUnload{
[super viewDidUnload];
self.myProperty = nil;
self.myOutlet = nil;
}
Now, I know that only the outlets and properties retained by the main view should be set to nil in viewDidUnload, and the rest properties should be released in dealloc. But hey, why do I have to bother for every property where it must be released - in dealloc or in viewDidUnload? If some property will be released twice it's OK because it wouldn't crash the app by sending a message to nil object. Putting release in both places (dealloc and unload) saves time and prevents from bugs later when doing code refactoring and forgetting to change release place. Any critics and shouting on that? :)
If you're using property accessors in -dealloc because you don't have access to the ivar directly, you should do the same in -dealloc that you do in -viewDidUnload:
self.myProperty = nil;
The point of using -release in -dealloc is to avoid calling the accessor, which conceivably could have been overridden by a subclass to have side effects that you don't want in -dealloc, when everything else from the subclass has already been deallocated. But if you're already calling the accessor in -dealloc, you might as well use the setter to release the ivar and ensure that it's done right.
The difference between -dealloc and -viewDidUnload is that you're still working with a complete, fully functional object in -viewDidUnload, whereas the object may already be partially deallocated in -dealloc.
My previous answer on this discusses what Apple recommends and why. Relevant portions reproduced here for clarity:
Also, from the Apple docs on -viewDidUnload:
The preferred way to relinquish ownership of any object (including those in outlets) is to use the corresponding accessor method to set the value of the object to nil. However, if you do not have an accessor method for a given object, you may have to release the object explicitly
So, there you go. If your outlet has a property associated with it (which they all should anymore), then nil it in -viewDidUnload -- but don't release it. This makes sense when you consider what is actually happening in a synthesized accessor; the code looks something like this:
- (void) setMyView1 : (UIView *) view {
if (myView1) // the associated IVAR is already set
[myView1 release];
myView1 = [view retain];
}
As you can see, setting a synthesize property to nil implicitly releases the retained object.
Also from the docs in regards to -dealloc:
If you implement this method but are building your application for iOS 2.x, your dealloc method should release each object but should also set the reference to that object to nil before calling super.
Unless you are supporting iOS2.x, there is no need to set objects to nil in dealloc.
So, to summarize Apple's docs regarding -viewDidUnload and -dealloc:
In -viewDidUnload, nil properties (including IBOutlet properties), but don't release them
In -dealloc release properties, but don't nil them (unless building for 2.x).
I've only recently noticed a crash in one of my apps when an object tried to message its delegate and the delegate had already been released.
At the moment, just before calling any delegate methods, I run this check:
if (delegate && [delegate respondsToSelector:...]){
[delegate ...];
}
But obviously this doesn't account for if the delegate isn't nil, but has been deallocated.
Besides setting the object's delegate to nil in the delegate's dealloc method, is there a way to check if the delegate has already been released just incase I no longer have a reference to the object.
No. There is no way to tell whether a variable points to a valid object. You need to structure your program so that this object's delegate isn't going away without letting it know first.
I assume you're not using GC. In that case, standard convention is that the code that sets the delegate is responsible for setting the delegate-user's reference to nil before allowing the delegate to be deallocated. If you're using GC, you can use a __weak reference for the delegate, allowing the garbage collector to set the reference to nil when the instance is garbage collected.
how about using a counter that you increment everytime you alloc and decrement everytime you dealloc. That way you could detect double allocs and could decide not to use a delegate if the counter isnt nil but the address isnt nil also
for debug proposes you can override release method on your class to see when it is called.
-(oneway void)release
{
NSLog(#"release called");
[super release];
}
I have a couple of questions relating to UIViewController:
1) When are each of the methods called for UIViewController? Specifically, the difference between viewDidLoad, viewDidUnload, and dealloc.
2) What's the difference, in general, when setting a pointer equal to nil and releasing the pointer? I know that in viewDidUnload you're supposed to set it equal to nil but in dealloc call release.
UPDATE: Sorry, just realized the question is misleading. Instead of dealloc, I meant -- when is initWithNibName:bundle: and release called? Just once by IB, right?
Setting a pointer to nil doesn't release the memory that it points to.
When you do something like
self.pointer = nil;
it's usually a case that the property has a retain attribute. When this is the case, setting the property to nil will indirectly cause a
[pointer release];
pointer = nil;
In the case of the view controller methods, viewDidLoad is called when your view is loaded, either from a nib, or programatically. More specifically, it's called just after -loadView is called. You shouldn't need to call loadView manually, the system will do it. The viewDidUnload method is called in the event of a memory warning and your view controller's view is not onscreen. Subsequently, loadView and viewDidLoad will get called again on demand.
The dealloc method, as normal, is called when your object's retain count reaches 0.
pointer = nil; // just clears the variable in which you store the pointer, but does not free memory.
[pointer release]; // just frees the object (memory), but does not clear the variable used to point to it.
self.pointer = nil; // sets the variable to nil. Also releases the object ONLY if pointer is a #property(retain) ivar.
One easy way to see when various methods are called is to do this in your UIViewController:
- (void)viewDidLoad
{
NSLog(#"MyViewController::viewDidLoad");
[super viewDidLoad];
// the rest of your viewDidLoad code, here.
}
// Etc., for the other methods of interest.
NOTE: much can be gleaned from overriding retain & release to log and then following along in the debugger.