---- question solved by myself, info updated in my comment ----
I have a view controller which has a periodic NSTimer. I call invalidate of the timer When I remove the view controller:
- (void)dealloc
{
NSLog(#"dealloc called");
if ([myTimer isValid]) {
[myTimer invalidate];
}
[super dealloc];
}
I discovered an unexpected behavior that the [myTimer invalidate] will immediately call my view controller's dealloc. That's why I put the isValid check to avoid crash. But the [super dealloc] will be called twice and crash the app.
Thus, I have two questions:
What's the proper way to invalidate a timer?
Why is the timer's invalidate method call the view controller's dealloc method?
Thanks
Leo
As described at CocoaDev: NSTimer:
Here are a couple of rules that might help you with NSTimer:
A timer retains the target and userInfo objects.
A timer is automatically retained by the run loop when scheduling it.
If a timer is not set to repeat, it will automatically invalidate itself upon firing.
A timer is released from the run loop when calling invalidate.
A timer releases the target and userInfo objects when calling invalidate.
In other words, if you release a repeating timer without invalidating it, it will continue to repeate because the run loop is retaining it. However, if you don't want to stop the timer before the application quits, or if the timer is non-repeating, you can release it after scheduling it without calling invalidate.
That sounds like some odd behaviour. Are there any other objects holding on to a reference to your view controller?
If not, there is a possibility that once the timer is removed & released from the run loop the view controller has nothing referencing it anymore (and so is deallocated).
Related
I am trying to dealloc / destroy an NSTimer after a user clicks back, but the deinit{...} inside the uiviewcontroller never gets called.
Beware that a view controller going out of the screen does not mean that it will be deallocated afterwards. I would recommend moving the timer dealloc to viewDidDisappear, but obviously it depends what you are using that timer for as well.
I am creating a repeating NSTimer object that calls the -Loop() method every iteration of the run loop:-
NSTimer *loopTimer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:#selector(Loop) userInfo:nil repeats:YES];
Within Loop() I am invalidating the timer when it becomes necessary:-
[loopTimer invalidate];
However, after using -invalidate(), the Touch handling methods -touchesBegan(), -touchesEnded() stop responding to touch events. Does NSTimer affect the run loop to somehow?
what else you are using in loop method...
I am not sure what problem exactly you have.Anyway try this...
You can use schedular method..
[self schedule:#selector(loop) interval:TIMER_INTERVAL];
If you unscheduling with in the same function,you can use
[self unschedule:_cmd];
otherwise,
You can use [self unschedule:#selector(loop)];
This sounds like you didn't correctly retain the view or have released it once too often. A NSTimer retains its target object, and once you invalidate a timer it releases its target object. In your case, that seems to make the retain counter drop to 0 and it thus gets deallocated.
You can verify this by adding a NSLog right in front of your [loopTimer invalidate]; and another one at the start of your view's dealloc. My bet is that you will see dealloc's log message immediately after your invalidate log.
No, it doesn't affect your runloop in anyway. Infact I checked it myself, after i invalidated the timer i was able to process touches on screen.So i guess problem is something else.
I seem to have fixed the problem by putting -invalidate() inside the -dealloc() method. So when i want end the loop i call [self.view removeFromSuperView] and this calls invalidate eventually.
Putting -invalidate() inside view lifecyle seems to deallocate the view...
In my application, I have pushed from oneviewcontroller to anotherviewcontroller. In that page I have started a timer and updated UITableview for every 1 sec (using UITableview reloaddata).
I have start the timer in viewwillappear function and invalidate timer in viewwilldisappear.
But when timer is running, when I came back to my viewcontroller my application crashed rapidly.
Please help me? Thanks in advance...
My guess is when i push back to viewcontroller that time tablereloads in previous viewcontroller that time it make crashes. I am using try catch but nothing makes any difference. Can we stop tableview reloads when we are not in that page?
The main reason of your app crashing is that, when-ever u come back form anotherViewController to oneViewController, your table view got release in between your timer still alive which is trying to update tableView and when you accessing the release object then app will get crashed. So u need to before stop your timer properly and nil it if timer isValid.
I hope mine answer will help you.
You strongly need to send invalidate message to all you timers at dealloc method of your class
- (void)dealloc
{
if ([_someTimer isValid]) [_someTimer invalidate];
}
I have a weird problem invalidating NSTimer. As long as the user is on a particular screen, I need to constantly update it. I'm using NSTimer to accomplish it. I wrote the below piece of code in viewDidLoad method.
- (void)viewDidLoad {
self.pollServerForUpdates = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(fetchNewDataFromServer:)
userInfo:nil
repeats:YES];
}
Problem is when I try to invalidate the timer. As I want the app to stop polling the server when the user leaves the screen, I put the timer invalidation code in viewWillDisappear method.
-(void) viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:YES];
//NSLog(#"%d",[self.view retainCount]);
[self.pollServerForUpdates invalidate];
self.pollServerForUpdates = nil;
}
I use a navigation controller to go back and forth between my main view and the polling view. The app crashes if I move between my main view and polling view real fast. I enabled the NSZombie to see whats happening and this is what I get
*** -[CALayer retainCount]: message sent to deallocated instance 0x1c3be0
If I remove the timer invalidation my app works fine. But I want to stop the polling (timer) as soon as the user leaves the screen.
I believe this is happening because the timer is called a split second before the view is released, how do I avoid it? Do I need to change my design? Any help will be appreciated.
p.s: I can't use push notifications on this screen.
It may be that the bug is somewhere else, with some other class that is using your view without holding a reference to it. If you don't invalidate your timer than it will have a reference to your view forever, potentially extending its lifespan and masking memory management bugs elsewhere in your code.
Try breaking on exceptions, and see where the call to the zombie is coming from.
If I have an NSTimer that starts in viewdidload, where is the proper place to invalidate it when leaving that view? Is it also necessary to release it as well?
If you create a timer with NSTimer scheduledTimerWithTimeInterval... then you don't need to release it, as you don't own it.
Now important thing is that the timer class retains the target, and in most cases we use self as the target. If the timer is not repetitive , then after the timer handler is completed, it automatically becomes invalid and the target is released. So you have nothing to do. But if your timer is still pending when leaving the view (this happens if you leave before the timer is fired or the timer is repetitive) then you need to invalidate it. The place MUST NOT be the dealloc method. As the timer itself retains the target, dealloc won't be called until the timer is invalid.
So it's better to invalidate when you know that you no longer need this. This might be the action which moves to the other view. Say user taps a button and in the button handler you move to other view. You can invalidate in this button handler.