During my work as an iOS developer, I’ve noticed that some people like to create repeating NSTimers in init/viewDidLoad and later attempting to invalidate them in dealloc/viewDidUnload. This is not a god habit, since it almost certainly introduces a bug that may not be apparent at first, but may cause either a memory leak or application to crash when a memory warning is received. I will explain why below.
The problem originates that the developer doesn’t realize that NSTimers retain their target. This is a hard fact that may not be apparent to novice developers, but it’s really important to know what effects that has on memory management. If the timer is set to repeat, it will retain it’s target (in this case a subclass of UIViewController) until invalidated. That means that even if the view controller is popped from the navigation hierarchy, the view controller won’t be deallocated since it’s still retained by the timer, and that’s not what you want, right?
If you created the timer in init, this will cause a memory leak. The intention may have been to invalidate the timer when the view controller is deallocated, but this will never happen as long as it’s retained by the timer (which is forever if the timer is repeating). So there’s your memory leak.
On the other hand, if you created the NSTimer in viewDidLoad you expected it to be released in viewDidUnload. This is correct, but it has some serious implications. First of all, viewDidUnload isn’t called when the view controller is deallocated (I could write another post about that) which may cause the same kind memory leak as above. Even if you manually call viewDidUnload in dealloc (which I like to do to avoid redundant code), you still have the problem that when the view controller is popped, the view controller won’t be deallocated since it’s (guess what?) retained by the timer. When you get a memory warning later on, viewDidLoad will be called, and the timer invalidated. The big problem here is that when the timer is invalidated, the view controller will be as well. The view controller is dead, and all subsequent attempts to access it’s instance variables, like setting the timer to nil, will result in an bad access that will cause the application will crash.
There you have it! So what’s the solution?
I like to create repeating timers in viewDidAppear and invalidate them and setting them to nil in viewWillDisappear. Since viewWillDisappear will always be called before you receive a memory warning that will cause the view controller to unload it’s view, you will never come to a stage where the timer is the sole retainer of the view controller, which is kind of the root of the problem.