I often find myself needing a repeating timer as part of a UIViewController
. For example - in my recent app Cosmos Timer on the alert sound selection screen, I wanted to check the mute switch status once a second. Depending on the status, I would show or hide a little warning message.
I initially had this in my viewDidLoad
method:
- (void) viewDidLoad;
{
mute = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self
selector:@selector(checkIfMuteIsOn)
userInfo:NULL
repeats:YES];
[mute retain];
}
And to clean-up the timer, I had:
- (void) viewDidUnLoad;
{
[super viewDidUnload];
[mute invalidate]; // stop timer
[mute release];
}
Seems reasonable, right? Well, no. The view controller’s dealloc
method was not being called. Nor was viewDidUnLoad
. So every time I accessed this screen, another repeating timer was being created to poll the state of the mute switch.
A quick test confirmed this – removing the NSTimer
, caused the view controller’s dealloc
method to be called as normal.
Looking closer at the docs for scheduledTimerWithTimeInterval:invocation:repeats:
- this line jumped out:
The timer instructs the invocation object to retain its arguments.
Ah, so the timer adds a retain
to the view controller. And until the NSTimer
is stopped, the view controller will never be dealloc’ed or unloaded. But I was stopping the timer in the unload method, so both the view controller and timer would never be destroyed. A circular reference.
So, what is the best way to add a repeating NSTimer
to a UIViewController
?
I settled on this:
- (void) viewWillAppear:(BOOL)animated;
{
[super viewWillAppear:animated];
// note: timer retained by target!
mute = [NSTimer scheduledTimerWithTimeInterval:1.0f
target:self
selector:@selector(checkIfMuteIsOn)
userInfo:nil
repeats:YES];
}
- (void) viewWillDisappear:(BOOL)animated;
{
[super viewWillDisappear:animated];
[mute invalidate];
}
The timer doesn’t need an explicit retain/release since it is retained by the UIViewController
target.
Permanent link to this post: http://xinsight.ca/blog/nstimer-in-a-uiviewcontroller/
Older: Reflections on Porting an iOS app to Android
Newer: Add copy functionality to a custom UIView