45

Note: This question is outdated—viewDidUnload is deprecated iOS 6.

When does UIViewController's viewDidUnload automatically get called? Yes I know, when the view unloads. But when does that happen automatically? How can I do it manually? Thanks.

mk12
  • 25,873
  • 32
  • 98
  • 137

5 Answers5

42

If you issue a memory warning in the simulator (look in the menu), this will get called for any view controller attached to a view that is not visible.

That's because view controllers by default are registered for memory warning notifications, and any view that is not currently being used will be unloaded by the view controller - the viewDidUnload method is there so that you can clean up anything else you would like, to save extra memory (or if you've retained some IBOutlets to help free up memory that would otherwise be released by the view being unloaded).

Generally any IBOutlets you release in dealloc, should also be released (and references set to nil) in this method.

Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
  • It won't get called for my app then because I only ever have one view controller instantiated at a time, when I switch them first I deallocate (self.vc = nil) the current one (after removing from superview). – mk12 Aug 17 '09 at 02:31
  • I don't use IBOutlets, I don't use IB. – mk12 Aug 17 '09 at 02:56
  • I'm pretty sure that you are correct, if you only have one VC at a time then the view would never be unloaded. The most common case is when you use a navigation controller, and the views you navigated away from are still there... Even if you don't use IB, if you add subviews that you keep references for the same rule would apply - nil out the reference (though again this seems not to apply in your case). – Kendall Helmstetter Gelner Aug 17 '09 at 19:18
  • +1 this helped me today. Why "should also be released (and references set to nil)?" If their `retain` properties, isn't setting them to nil good enough to achieve all these goals. – Dan Rosenstark Jan 14 '11 at 16:28
  • 1
    Yes, setting the properties to nil kills both birds with one stone (releases and unsets references). – Kendall Helmstetter Gelner Jan 15 '11 at 02:54
  • I push another `UIViewController` onto my `UINavigationController`’s stack, and then “Simulate Memory Warning”. `viewDidUnload` for the non-visible `UIViewController` is *not* called… – mxcl Nov 03 '11 at 18:01
  • Did you load the other view controller from a nib? Perhaps it is not called if it's wholly generated in code (though I would doubt that). I have never seen it not call viewDidUnload in the case you are describing... – Kendall Helmstetter Gelner Nov 03 '11 at 18:19
  • Why do I have to nil the IB outlets in -dealloc if I already do so in -viewDidUnload? Isn't it redundant? – Morrowless Jan 13 '12 at 01:34
  • Not redundant, viewDidUnload is called only on a memory warning, and not when the object is deallocated... dealloc is called only when the view controller is deallocated. That said, I do usually create one method to nil instance vars that I call from both locations. Just remember not everything should be released on a memory warning that gets released on dealloc. – Kendall Helmstetter Gelner Jan 13 '12 at 04:48
  • 2
    @mk12 bro, don't hate on IB. I use NIBs whenever I can because it drops development time significantly. less development time = more you get to pocket. Plus, UI designers love you when you use it (making you more marketable). Jast passing some love your way! – Jacksonkr Mar 29 '12 at 13:46
37

In addition to manually issuing a memory warning in the simulator, you can issue one programatically with

- (void)_simulateLowMemoryWarning {
  // Send out MemoryWarningNotification
  [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification
                                                      object:[UIApplication sharedApplication]];
  // Manually call applicationDidReceiveMemoryWarning
  [[[UIApplication sharedApplication] delegate] applicationDidReceiveMemoryWarning:[UIApplication sharedApplication]];
}

You can then cause this to happen every 5 seconds using a timer

static NSTimer *gLowMemoryTimer = nil;

- (void)stopLowMemoryTimer {
  [gLowMemoryTimer invalidate];
  gLowMemoryTimer = nil;
}

- (void)startLowMemoryTimer {
  if (gLowMemoryTimer) {
    [self _stopLowMemoryTimer];
  }
  gLowMemoryTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(_simulateLowMemoryWarning) userInfo:nil repeats:YES];
}
johnboiles
  • 3,494
  • 1
  • 32
  • 26
  • 6
    Brilliant idea to test an app. – Guillaume Dec 09 '10 at 15:33
  • This is a great idea, though since this would only be used for testing, it would probably be best to use the private method in UIApplication that gets called when a real memory warning happens, just in case it does anything else: `[[UIApplication sharedApplication] performSelector:@selector(_performMemoryWarning)];` – Ben Baron Oct 16 '12 at 19:16
17

-viewDidUnload is called whenever the viewcontroller's view property is set to nil, either manually or most commonly through didReceiveMemoryWarning:.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • I have a scenario where I want -viewDidUnload to fire on my UIViewController that was hosted by a UIPopoverController. After the popover has been dismissed, I am manually nil'ing out the view of the view controller, but -viewDidUnload is still not fired. Any ideas why not? – dreyln Jun 09 '11 at 14:52
  • Did you put a breakpoint in viewDidUnload to confirm that it's not firing? Make sure that your code that sets the view to nil actually references the correct view controller (check that the view controller isn't itself nil at that point, and that its value is the same as the value you expect). The most common cause of these kinds of problems is accidentally an extra view controller that isn't on screen. – Rob Napier Jun 09 '11 at 16:42
  • Yes, I did have a breakpoint and some code that invalidates a timer. The breakpoint was never hit and my timer kept going (was logging messages to the console). I also verified that the contentViewController of my popover was the correct one and not nil. Wonder if view is still technically visible. I'm nil'ing out the view in the -popoverControllerDidDismissPopover: method. – dreyln Jun 09 '11 at 17:19
  • So you're calling `popoverController.contentViewController.view = nil`, but `[contentViewController viewDidUnload]` never fires? That is surprising. If "didDismiss" fires, then the view should be offscreen. I don't know an obvious problem, but I'd build a small test harness to investigate what normally happens. – Rob Napier Jun 09 '11 at 18:49
  • yes, that is exactly what I did. I ended up moving the code I wanted run from -viewDidUnload to -viewDidDisappear:animated – dreyln Jun 09 '11 at 18:54
10

iOS 6.x and later

I know this is an older question, but I feel an answer should be submitted regarding the changes to the viewDidUnload API in iOS 6 namely that in iOS 6 viewDidUnload is no longer called (at all) and has been deprecated.

Beltalowda
  • 4,558
  • 2
  • 25
  • 33
  • 2
    Further to this: if you deploy against iOS 5 you should still write a `viewDidUnload` implementation, but it is vital to not have any logic that would need to run on iOS 6 there. – mxcl Oct 26 '12 at 14:32
3

viewDidUnload called in low memory conditions. We should unload stuff that we loaded in viewDidLoad method. We need to relinquish ownership of object by calling accessor method to set it to nil. In case of an outlet, the object release itself so the object reference can be set safely to nil. If not a synthesized property, then we first need to release object than we set to nil.

Vipin_iOSdev
  • 167
  • 1
  • 2
  • 7