views:

169

answers:

5

Does any one know when is the best time to stop an NSTimer that is held reference inside of a UIViewController to avoid retain cycle between the timer and the controller?

Here is the question in more details: I have an NSTimer inside of a UIViewController.

During ViewDidLoad of the view controller, I start the timer:

statusTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(updateStatus) userInfo: nil repeats: YES];

The above causes the timer to hold a reference to the view controller.

Now I want to release my controller (parent controller releases it for example)

the question is: where can I put the call to [statusTimer invalidate] to force the timer to release the reference to the controller?

I tried putting it in ViewDidUnload, but that does not get fired until the view receives a memory warning, so not a good place. I tried dealloc, but dealloc will never get called as long as the timer is alive (chicken & egg problem).

Any good suggestions?

A: 

The -viewDidDisappear method may be what you're looking for. It's called whenever the view is hidden or dismissed.

Josh Lindsey
But viewDidDisappear never gets called when I hide my parent view using: [self.view removeFromSuperview].Any other suggestion?
In addition, when inside a UINavigationController, viewDidDisappear does not gets called, so I'm looking for a more general place to put it.
A: 

You can try with - (void)viewDidDisappear:(BOOL)animated and then you should validate it again in - (void)viewDidAppear:(BOOL)animated

More here

vodkhang
when inside a UINavigationController, viewDidDisappear does not gets called, so I'm looking for a more general place to put it, also when I hide the view using [self.view removeFromSuperview], viewDidDisappear does not get called either.
what do you mean by inside a UINavigationController. when you pop it out? Can you post some more code about that
vodkhang
A: 

I wrote a "weak reference" class for exactly this reason. It subclasses NSObject, but forwards all methods that NSObject doesn't support to a target object. The timer retains the weakref, but the weakref doesn't retain its target, so there's no retain cycle.

The target calls [weakref clear] and [timer invalidate] or so in dealloc. Icky, isn't it?

(The next obvious thing is to write your own timer class that handles all of this for you.)

tc.
+1  A: 
  1. You could avoid the retain cycle to begin with by, e.g., aiming the timer at a StatusUpdate object that holds a non-retained (weak) reference to your controller, or by having a StatusUpdater that is initialized with a pointer your controller, holds a weak reference to that, and sets up the timer for you.

    • You could have the view stop the timer in -willMoveToWindow: when the target window is nil (which should handle the counterexample to -viewDidDisappear: that you provided) as well as in -viewDidDisappear:. This does mean your view is reaching back into your controller; you could avoid reaching in to grab the timer by just send the controller a -view:willMoveToWindow: message or by posting a notification, if you care.

    • Presumably, you're the one causing the view to be removed from the window, so you could add a line to stop the timer alongside the line that evicts the view.

    • You could use a non-repeating timer. It will invalidate as soon as it fires. You can then test in the callback whether a new non-repeating timer should be created, and, if so, create it. The unwanted retain cycle will then only keep the timer and controller pair around till the next fire date. With a 1 second fire date, you wouldn't have much to worry about.

Every suggestion but the first is a way to live with the retain cycle and break it at the appropriate time. The first suggestion actually avoids the retain cycle.

Jeremy W. Sherman
A: 

You can write this code in dealloc function of view controller

for eg.

-(void)dealloc
{
   if([statusTimer isValid])
  {
       [statusTimer inValidate];
       [statustimer release];
      statusTimer = nil;
  }
}

this way the reference counter of statustimer will automatically decrement by 1 & also the data on the allocated memory will also erase

also you can write this code in - (void)viewDidDisappear:(BOOL)animated function

Tarun Mittal
-1 The problem is that `dealloc` will never be called because the `NSTimer` is keeping the `UIViewController` alive.
Jon-Eric