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...
Related
---- 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).
If I create a timer which never repeats
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(updateSystems) userInfo:nil repeats:NO];
do I still need to invalidate it?
Also, is releasing the instance one step in the invalidate method? Since I'm not using alloc my timer variable shouldn't have to be released, although when creating a timer it automatically should create a new thread? Does the timer still add up on the stack?
I need some clarity.
Thank you
See the documentation for the method. It says:
repeats
If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.
You are not creating any pointer to NSTimer. You are using it directly through a class method. This method does not creates a new instance of NSTimer so you don't have to release it.
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.
I am currently working on a project where I request and parse multiple html sites in a controller. To give some feedback to the user I have created a second view, which gets displayed during processing of the data. It displays a status label and a progressbar. In my controller I have several points where I update my labels text. Unfortunately this works only sometimes. I guess thats because the label gets redrawn only once in a while and I try to do it probably more often than once a second.
It does not work to force a redraw with [label setNeedsDisplay];
I also made a thread which updates the labels text with the text of a global variable and also calls setNeedsDisplay on the label every second. But the results in the same. I only see certain changes, but not all.
Everything is setup properly and the label never is nil. When I log my updateMethod everything seems allright. It just does not get displayed!
Cheers
Here is the code of my threads
- (void)startUpdateStatusThread{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(updateFrequently) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void)updateFrequently{
NSLog(#"updateFrequently: %#", currentStatus);
test++;
[self.statusLabel setText:[NSString stringWithFormat:#"%# - %i", currentStatus, test]];
[self.statusLabel setNeedsDisplay];
[NSTimer scheduledTimerWithTimeInterval:0.0001 target:self selector:#selector(updateFrequently) userInfo:nil repeats:NO];
}
Am I right if I assume that you call your label's text-property as well as setNeedsDisplay from within your "Thread" that is parsing your websites?
Note that: changes to UIViews (or subclasses of UIView like your label) must be performed on the main thread.
What I recommend you to do is write a helper method that updates your label and calls setNeedDisplay, the from within your Parser-Thread call
[mainViewController performSelectorOnMainThread:#selector(yourUpdateLabelHelper:) withObject:nil waitUntilDone:NO];
that should get your job done.
You mention threads. Be aware that UIKit controls, such as UILabel, can only be updated from the main thread. Make sure you are only attempting to set the label's text from the main thread and then verify if you are still having issues.
EDIT due to question edit:
First, -setNeedsDisplay only tells the view it needs to redisplay the next time the screen is refreshed, it does not force a display at that time.
Second, the iPhone screen draws at about 60 hz. This means there is an update every 0.016666 or so seconds. You're trying to update much faster than that, so only about every 160th of your updates will actually be displayed.
Third, and probably most important, you don't seem to be making any threads, unless you call -startUpdateStatusThread from a background thread. Either way, once you hit -updateFrequently everything is back on the main thread. With the frequency you are scheduling that timer, you are probably overloading the CPU and making it so that the system does not have time to draw the label. So even though you are setting new text, you aren't giving it a chance to render. You need to reduce the frequency of that timer, and you probably need to rethink whatever it is you're trying to do.
I think that the creation of the timer should be in separate function:
Allow the repetition and also store the timer in a member variable you can close on dealloc.
[NSTimer scheduledTimerWithTimeInterval:0.0001 target:self selector:#selector(updateFrequently) userInfo:nil repeats:YES];
}
The update function should operate as a callback function.
Regards
Assayag
I'm making a simple iPhone app whose sole function is to update a UIView forever (until it exits).
I tried this in applicationDidFinishLaunching and viewDidLoad:
while(1) {
// update view here
}
but that doesn't work- the app never finishes loading. I'm sure there's a simple solution to this, I just don't know what it its.
Also: ideally, this process should consume very little resources.
You can't have a while (1) statement like there, as it not allow viewDidLoad to return, and your app will never get any other calls such as tap processing, screen draw updates, etc.
In viewDidLoad, set up a timer task using:
updateTimer = [NSTimer scheduledTimerWithTimeInterval: kUpdateTimeInterval target:self selector:#selector(updateView) userInfo:nil repeats:YES];
And have a method called updateView that actually does the updating. Instead of updateView you could also use setNeedsDisplay which will trigger a call the -drawRect method of your view class, and do the actual drawing there.
What ends up happening now is that your viewDidLoad will set up a repeating task and at every kUpdateInterval, your view will be updated.
Instead of updating the view all the time, maybe you could just call its -setNeedsDisplay method when the data that you're displaying changes.