I make an asynchronous http request using NSURLConnection every 11 seconds using a NSTimer.
The NSTimer runs on the main run loop. The request has a timeout of 10 seconds.
The NSTimer works well until I increase UI activity on the app (like tapping on UIButtons, dismissing UIAlertViews frequently).
After this the NSTimer speeds up and runs like a while loop without any delays. This creates a lot of problems in the app as I can't make proper handling of the connection responses.
The problem is that you are declaring an NSTimer inside your button action... now every time this button is pressed the NSTimer interval is compiled onto its previous interval and the result is that it gets called twice as often... the next time it's twice as often than the previous twice as often.... etc.
The best way to do it is define the NSTimer in the .h file and then every time you have code in your .m file to call it first check if it's already there and if so invalidate and release it.
if(myTimer){
[myTimer invalidate];
[myTimer release];
}
myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timeInterval) userInfo:nil repeats:YES];
Related
My application needs to show a temporary message, so I created Toast like android myself. It works well for only one toast. I am using NSTimer to hide the message. If I display more than one toast the timer becomes a problem. Only the last added toast becomes hidden, others do not become hidden. How can I hide all toasts?
Code:
Remove function:
-(void)removeToast
{
NSLog(#"removed");
[self.view removeFromSuperview];
}
Timer start:
timer = [NSTimer scheduledTimerWithTimeInterval:(4.0f)
target:self
selector:#selector(xxxx)
userInfo:nil repeats:NO];
I wrote this in above in separate NSObject class and created an object. I need some clarification on how run the two NSTimer simultaneously or keep track of all NSObjects.
Object creation in viewcontroller is
#property(nonatomic,strong)Toast *toast;
No need to keep the istance of timer in a ivar for your purpose. Use the userinfo: parameter to pass the view you mean to hide when the timer fires, like this:
[NSTimer scheduledTimerWithTimeInterval:(4.0f)
target:self
selector:#selector(xxxx)
userInfo:yourView repeats:NO];
Then in your selector retrieve the view from the userInfo and hide it. You can find a working sample here, line 37.
I have created an animated game app, In which I am using NSTimer to move an image after a particular interval of time.
timer = [NSTimer scheduledTimerWithTImerInterval:0.2 target:self selector:#selector(MoveVirus) userinfo:nil repeats:YES];
this timer calls up the function MoveVirus, MoverVirus moves virus (an Image on screen).
It works fine in the beginning but the speed of motion automatically increases.
The increased speed of motion destroys every further logic.
I don't know what is the problem with it?
Please help to solve this problem.
NSTimer isn't necessarily meant for this sort of use...From the docs on NSTimer:
A timer is not a real-time mechanism; it fires only when one of the
run loop modes to which the timer has been added is running and able
to check if the timer’s firing time has passed. Because of the various
input sources a typical run loop manages, the effective resolution of
the time interval for a timer is limited to on the order of 50-100
milliseconds. If a timer’s firing time occurs while the run loop is in
a mode that is not monitoring the timer or during a long callout, the
timer does not fire until the next time the run loop checks the timer.
Therefore, the actual time at which the timer fires potentially can be
a significant period of time after the scheduled firing time.
A better approach, if you are moving UIImages that are contained in a UIImageView would be to use the class animation methods on UIView. You can still get the same result of moving the image after a particular time if you use the animation method that contains a delay. The method is:
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
Using this method, you can specify the time of the animation (how long it takes to animate the move of your UIImages), how long to wait before starting this animation, a set on animation options, a block of animation code, and a block that executes when the animation is complete.
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 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...
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.