
By Stan Chang · · Updated · 2 min read · #ios-development
viewDidUnload Was Never the Opposite of viewDidLoad
A lifecycle trap from iOS 5 and earlier
#ios #uiviewcontroller
viewDidUnload in iOS 6. Views are no longer purged under low-memory conditions, so the method is never called. Do not implement it in a current UIKit app.When I first documented this in 2011, the trap was in the name. viewDidUnload sounded like the lifecycle counterpart to viewDidLoad, but it was never a general teardown or deallocation callback.
What viewDidUnload actually did
In iOS 5 and earlier, UIKit could release an off-screen view controller’s view during a low-memory condition. It then called viewDidUnload so the controller could release references to views and other objects that were cheap to recreate.
The callback did not run whenever the view controller itself was deallocated. In a manual reference-counting codebase, cleanup placed only in viewDidUnload therefore missed the ordinary deallocation path. Retained view references still needed to be released in dealloc.
The safe historical split was:
viewDidUnload: release recreatable view objects after UIKit purged the view;dealloc: release every object owned by the controller;- application state: keep it outside
viewDidUnload, because UIKit could recreate the view later.
Why relying on it caused leaks
Manual reference counting trained Objective-C developers to balance ownership calls. The names viewDidLoad and viewDidUnload encouraged the same mental model: allocate views in one method and release them in the other.
That covered only the low-memory unload path. If the controller was deallocated without receiving viewDidUnload, anything released only there stayed retained. That was the narrow bug in the original article—not that viewDidUnload itself leaked views, but that treating it as guaranteed teardown left the ordinary deallocation path incomplete.
What to do in current UIKit
There is no modern replacement callback for unloading a view controller’s view, because UIKit no longer performs that lifecycle transition. Let normal ownership and ARC release the view hierarchy when the controller is deallocated.
If an app holds large caches or other recreatable resources, treat those resources explicitly when responding to memory pressure. Do not add viewDidUnload to current code and do not move ordinary lifecycle cleanup into a low-memory callback.
Apple’s current viewDidUnload documentation preserves the old behavior and marks the method deprecated, which makes it the right reference when maintaining pre-iOS-6 code. For current view-controller memory-pressure handling, see didReceiveMemoryWarning().
The durable lesson is to follow ownership paths, not symmetry in lifecycle names: cleanup must exist on every path that releases the owner.