I've found on Apple documentation pages an example in which they deallocate the memory as follows:
- (void)viewDidUnload
{
self.mapAnnotations = nil;
[super viewDidUnload];
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)dealloc
{
[mapAnnotations release];
[super dealloc];
}
I was wondering why
they first set mapAnnotation to nil in viewDidUnload and then they release in dealloc method and
why they refer using to mapAnnotation with and without self.
This is the downloadable example by the way: MapCallouts Example
The question you should be asking is: when is viewDidUnload called?
In short it's called in low memory situations where you don't want to deallocate the whole controller, i.e., when you don't want dealloc to be called.
You can think of viewDidUnload as the opposite of viewDidLoad of loadView whereas dealloc is the opposite of init. (In practice it can get a bit more complicated than that of course.)
viewDidUnload can be thought of as the opposite of viewDidLoad. It is called in cases where the view is unloaded due to memory warnings, but the view controller is not actually deallocated.
It allows you to release memory that is only relevant when the view is loaded, and therefore allows you to free up memory in these low memory conditions.
As for the difference between the two releases, one using self and one not:
In the viewDidUnload method, the variable is being set to nil via it's accessor methods. The variable has been declared as a property, likely with the retain attribute. When assigning nil via a property, it's functionally the same as the following code:
- (void)setMyObject:(MyObject *)object
{
[myObject release];
myObject = [object retain];
}
So if you're passing nil to that method, you'll be releasing the old instance and assigning nil to the variable. Assigning nil after releasing is considered good practise to prevent any possibility of trying to dereference a dangling pointer.
In the dealloc method, the variable is accessed directly, as opposed to via an accessor method or property, and is released. There is not really any need to assign nil at this point because the owning object will be deallocated and will not be accessing any of its pointers, making the possibility of dereferencing a dangling pointer very low.
(1) This is done so you can let go of references so the system can deallocate resources in low memory situations. It is the reciprocal of viewDidLoad:
(2) If you refer to self using dot notation, you will maybe create an object that is the implementation detail of the property. Using the name ensures that you release the object itself, and not the object returned by the property. In the first example, in the case of an outlet, assigning nil with the dot notation (self.) will decrement the retain count of the retained property.
Related
Hi All
I am trying to dealloc a ViewController in ARC mode.
However, the RefCount is always non-zero.
I have tried to set all object to nil and all subviews to removeFromSuperview + nil;
and timer to invalidate + nil;
still the counter = 2;
Is there a way to trace which pointer is still in retain?
Thanks
If you are using blocks you might also create retain cycle there. E.g. a block is referenced by an object and inside this block you are referencing object or calling instance method for object.
Another option for retain count not dropping to 0 is that you have registered abject as observer for notification.
You might find this answer helpful:
https://stackoverflow.com/a/12286739/2261423
Example of strong reference cycle from apple docs:
self.block = ^{
[self doSomething]; // capturing a strong reference to self
// creates a strong reference cycle
};
#Billy, why are you doing this? You may not worry about deallocation when using ARC. Controller will be deallocated automatically, when there will be no references to the controller. Yes, views do not refer to controller, they are referred by it! So removing that views will not affect retain count of the controller. If you really want to remove View Controller from memory, remove it from parent view controller and set all links to nill.
So with viewDidUnload deprecated as of iOS 6, what do I need to do now?
Delete it, and migrate all of it's contents in didReceiveMemoryWarning, or leave it, and don't do anything in didReceiveMemoryWarning?
The short answer is that, in many cases, you don't need to change anything. And, you most certainly do not want to simply migrate all of the contents of viewDidUnload to didReceiveMemoryWarning.
Generally, most of us do the setting of IBOutlet references to nil in viewDidUnload (largely because Interface Builder would put that there for us) and do the general freeing of memory (e.g. clearing of caches, releasing of easily recreated model data, etc.) in didReceiveMemoryWarning. If that's the way you do it, then you probably don't require any code changes.
According to the iOS 6 viewDidUnload documentation:
Views are no longer purged under low-memory conditions and so this method is never called.
Therefore, you do not want to move the setting of your IBOutlet references to nil anywhere, because the views are no longer purged. It would make no sense to set them to nil in didReceiveMemoryWarning or anything like that.
But, if you were responding to low memory events by releasing easily-recreated model objects, emptying caches, etc., in viewDidUnload, then that stuff should definitely move to didReceiveMemoryWarning. But then, again, most of us already had it there already.
Finally, if you free anything in didReceiveMemoryWarning, just make sure your code doesn't rely upon them being recreated in viewDidLoad again when you pop back, because that will not be called (since the view, itself, was never unloaded).
As applefreak says, it depends upon what you were doing in viewDidUnload. If you update your question with explicit examples of what you had in your viewDidUnload, we can probably provide less abstract counsel.
The short answer:
Never use -didReceiveMemoryWarning for a balanced tear down as it might get called never or multiple times. If you have your setup in -viewDidLoad, place your cleanup code in -dealloc.
The long answer:
It's not easy to give a general answer, as it really depends on the situation. However, there are two important facts to state:
1. -viewDidUnload is deprecated and in fact never called starting with iOS6 and later. So, if you have your cleanup code in there, your app leaks under these OS versions
2. -didReceiveMemoryWarning might be called multiple times or never. So it's a really bad place for a balanced teardown of objects you created somewhere else
My answer is looking at the common case where you are using properties, for example:
#property (strong) UIView *myCustomView // <-- this is what I'm talking about
#property (assign) id *myDelegate
Here you have to do some cleanup, because you either created and own the customView or InterfaceBuilder created it, but you are retaining it. Before iOS 6 you would probably have done something like this:
- (void)viewDidLoad {
self.myCustomView = [[UIView alloc] initWithFrame:…];
}
- (void)viewDidUnload { // <-- deprecated!
[myCustomView removeFromSuperView];
self.myCustomView = nil;
}
...because (again) myCustomView is a retained property, created and owned by you and so you have to take care and "release" it (set it to nil) at the end.
With iOS 6, the best place to replace -viewDidUnload and to set the retained property to nil is probably -dealloc. There's also viewWillAppear and viewDidDisappear, but these are not tied to the lifecycle of your view/controller, but the display cycle (On the other hand, the -...appear methods are perfect for un-/registering notification listeners!). So it might not be appropriate to create and destroy views before and after every display. dealloc is the only method we can be sure of to be called at the very end of the controller's lifecycle. Note that you must not call [super dealloc] if you're using ARC:
- (void)dealloc {
self.myCustomView = nil;
}
However, if you're using viewDidLoad to do some view related setup that can be freed upon low memory conditions, the other posts showing how to deal with low memory situations are totally valid. In this case you can also use dealloc, but you have to check if your views are still there.
The Lifecycle of a ViewController
Maybe it's also helpful to look a the general lifecycle of a ViewController:
This is the lifetime of a viewController (lines in italic mean that these methods might be called multiple times):
init: ViewController loaded, no interface element (IBOutlet) available yet (all nil)
viewDidLoad: the nib/storyboard has been loaded and all objects are available. The user sees nothing yet
viewWillAppear: the view is about to be displayed
viewDidAppear: the view is on screen
viewWillDisappear: the view is about to go away
viewDidDisappear: the view just was taken off the window
viewDidUnload: NEVER CALLED in iOS6/7
didReceiveMemoryWarning: You don’t know if, when and how often this is called. Prior to iOS6 it might unload the view, after iOS6 it just purges an offscreen cache or does nothing
dealloc: the viewController is about to get destroyed
So, to sum it up there are various possibilities; what goes where now really depends on what was initialized where:
-dealloc if created in -init: or -viewDidLoad:
-viewWill/DidDisappear (paired with -viewWill/DidAppear)
-didReceiveMemoryWarning (might or might not be called)
If you need to know if your UIViewController is dismissing you can add this code to your viewWillDisappear:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if ([self isBeingDismissed] || [self isMovingFromParentViewController])
{
// Do your viewDidUnload stuff
}
}
It is not called at the same time as viewDidUnload did once in the view controller life cycle, but works in most cases for your needs!
Depends on what you do in viewDidUnload but you can use didReceiveMemoryWarning or dealloc to release data. See this.
In most typical cases, this method can be used in place of the old viewDidUnload.
// The completion handler, if provided, will be invoked after the dismissed controller's viewDidDisappear: callback is invoked.
- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion NS_AVAILABLE_IOS(5_0);
Swift 2017 syntax:
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
if (yourChildViewController != nil) {
print("good thing we did this!")
}
yourChildViewControllerProperty = nil
super.dismiss(animated: flag, completion: completion)
}
Prior to iOS 6, we were supposed to
- (void)viewDidUnload {
self.someDelegate = nil;
[super viewDidUnload];
}
Now that viewDidUnload is deprecated, where do we set our delegates to nil? Thanks!
Views are no longer unloaded when receiving a memory warning in iOS6. So the view is never unloaded on such cases and viewDidUnload is never called in iOS6.
If you really want to mimic the old behavior (unloading the view upon receiving a memory warning) you now have to implement this behavior in the didReceiveMemoryWarning method of your view controller, after testing that the self.view.window property is nil (meaning the view is not on screen anymore, thus will be "unloaded", meaning that you are in the same situation as the old viewDidUnload case).
-(void)didReceiveMemoryWarning
{
if (self.isViewLoaded && !self.view.window)
{
// If view already loaded but not displayed on screen at this time (not attached to any window) then unload it
self.view = nil;
// Then do here what you used to do in viewDidUnload
self.someDelegate = nil;
...
}
[super didReceiveMemoryWarning];
}
But note that if you use ARC and iOS5+, you generally won't need to set your delegate to nil anymore, thanks to the weak property attribute and the Zeroing-Weak-References mechanism that automatically reset weak variables and properties to nil if the object they are pointing to does not exist anymore (thus avoiding dangling pointers).
[EDIT] And as explained by #Martin R in the comments, views are not unloaded anymore when receiving a memory warning in iOS6, so you won't have to manage this case of receiving a memory warning and think about releasing your delegate there as this use case won't occur anymore in iOS6.
Well, I did not come up with this myself, but if should help you: "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."
http://www.bgr.com/2012/06/11/ios-6-beta-download-link-iphone-ipad-ipod-touch-release/
Prior to iOS 6, we were supposed to
(void)viewDidUnload {
self.someDelegate = nil;
[super viewDidUnload];
}
Where did you hear this?
Do you even know what viewDidUnload is for prior to iOS 6?
viewDidUnload is only called in low memory situations, which cause the view to be unloaded. It will never be used during normal operation. If you were depending for it to be called to do other things, that is wrong.
Plus, why would you ever need to set self's delegate to nil anyway? "Niling the delegate" refers to setting other objects' delegates (which point to self) to nil when self is deallocated. Setting self's delegate makes no sense (why would you care setting stuff on self if self is no longer used?).
Should the Variables with the #property nonatomic and retain be released explicitly.
#interface MyScreenViewController : UIViewController <UIWebViewDelegate> {
UIWebView* greetingView;
}
#property(nonatomic, retain) IBOutlet UIWebView* greetingView;
- (void)dealloc {
[greetingView release];
}
Is release in delloc method Required ???
Ofcourse. YES you should release your properties with retain,copy and don't for assign. You set the property to Nil in viewDidUnload.
- (void)viewDidUnload {
[super viewDidUnload];
self.greetingView = nil;
}
- (void)dealloc {
[greetingView release],greetingView = nil;
[super dealloc];
}
Refer memory management in UIViewController Class reference: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html
Memory is a critical resource in iOS, and view controllers provide built-in support for reducing their memory footprint at critical times. The UIViewController class provides some automatic handling of low-memory conditions through its didReceiveMemoryWarning method, which releases unneeded memory. Prior to iOS 3.0, this method was the only way to release additional memory associated with your custom view controller class but in iOS 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.
Yes, you should release it, because it is a retained property. Anything property that is retained (or copied) should be released by the same class (if it is assigned, it doesn't need to be released).
Retain is basically like a class saying, "I am going to be using this other class, so keep it around in memory." dealloc is where the class that said that is, itself, going to be removed from memory. So if the class that needed your retained property is about to go away, that object itself should be deallocated, too.
Otherwise it is just going to sit there in memory, when nothing else in the program needs it. And if that kind of thing keeps happening, you will run out of memory.
(Also, make sure you that you call [super dealloc] at the end of of your dealloc method.)
I have a View Controller as part of a navigation controller stack with two IBOutlets. In viewDidUnload I free them up:
- (void)viewDidUnload
{
self.myView1 = nil;
self.myView2 = nil;
[super viewDidUnload];
}
But I still had a leak. So I stuck release messages in the dealloc for them too:
- (void)dealloc
{
[myView1 release];
[myView2 release];
[super dealloc];
}
This appears to clear the memory leak. However, I was always told that I should only release ivars that I have created using alloc, copy or new. So I'm worried about these two releases being in here. Is this right or wrong? Can someone please explain this to me because I keep getting conflicting opinions... Thanks!
If any of your #property objects are declared retain or copy, you need to release them in dealloc. This includes your outlets.
By using IBOutlet, the variables are exposed to be connected in Interface Builder and allocated when the view controller is initialized. So they have to be released and deallocated, as the view controller is unloaded and deallocated. Since most IBOutlets are retained UI* properties, this is necessary.
Assigning nils to the variables is technically not deallocating. It's simply the last state to have retain count 0, just before actually being deallocated.
Also, please note they are referenced using self. It means, the references from the view controller becomes nil, not the allocations.
So in conclusion, IBOutlet properties must be released in dealloc()
(Though I am quite confident, someone else may provide 100% correct answer for this.)
The basic, safe pattern is
declare ivar
declare IBOutlet property for ivar
release property in dealloc
only reference property, never ivar
The xib sets the property, which release whatever might have been there first.
I'm a bit confused why that leak was there though. Setting the property to nil should release the old reference. Perhaps viewDidUnload wasn't even getting called? Are you sure you even need viewDidUnload?