Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I want to set a timer with a constantly changing delay which is stored in a variable. How would I do this, if I set a timer within a timer to update the delay it doesn't work at all.
Thanks
Don't have a repeating timer. Use a run once timer, and when it completes/runs, create another timer with the new delay.
Or... as #Steve Wilford noted in his answer, use setFireDate: on NSTimer. From the docs "Adjusting the firing time of a single timer would likely incur less expense than creating multiple timer objects, scheduling each one on a run loop, and then destroying them."
In your class interface use
#interface YourClass : SuperClass <Delegates> {
NSTimer *yourTimer;
NSInteger delay;
}
And when you set your timer make sure you set
delay = 1.0;
yourTimer = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:#selector(methodToCall) userInfo:nil repeats:false];
And finally in your methodToCall
-(void)methodToCall {
// Do stuff and reset timer with delay variable
yourTimer = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:#selector(methodToCall) userInfo:nil repeats:false];
}
This means that the timer will have to run down to start running again with a new delay so this might not exactly be the solution you are after if you want something more instantaneous, but changing delay will mean that when the timer runs down it will begin again with your new delay
This also still allows you to call [yourTimer invalidate] and not have repeating timers with no pointer
Hope that helps, or maybe will point you to the solution you are looking for
Maybe the setFireDate: method of NSTimer will help?
Note that it is a "relatively expensive" operation so you should consider if this is actually what you want to be doing.
bandejapaisa has the right idea, but you might need to create the new timer from the main thread. Use:
[self performSelectorOnMainThread:<#(SEL)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#>]
to create the new timer.
Related
This question already has answers here:
Prevent dispatch_after() background task from being executed
(11 answers)
Closed 8 years ago.
Is there a way to cancel dispatch_after() scheduled for some time in future, and haven't fired so far?
I'm trying to make something like a scheduler for updates from server, and this method is just like I want, but, I'd love to cancel and re-schedule it at some point.
Is it possible at all or I have to fallback and use NSTimer?
There is NO way to prevent a dispatch_block from executing once it has been dispatch to it's queue, meaning that your dispatch_after cannot be canceled. Only option is to add in your block a condition to be checked at runtime to prevent execution.
ie.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), ^ {
if(self.shouldExecuteDispatchBlock)
{ // do your stuff } });
OK, so, with all answers collected, and possible solutions, seems like the best one for this case (preserving simplicity) is calling performSelector:withObject:afterDelay: and cancelling it with cancelPreviousPerformRequestsWithTarget: call when desired. In my case - just before scheduling next delayed call:
[NSObject cancelPreviousPerformRequestsWithTarget: self selector:#selector(myDelayedMethod) object: self];
[self performSelector:#selector(myDelayedMethod) withObject: self afterDelay: desiredDelay];
For this purpose i used this class:
https://github.com/SebastienThiebaud/dispatch_cancelable_block
you can call a cancel() function to revoke the execution of what's in the block.
Use a dispatch timer source (that is what dispatch_after uses internally anyway).
A dispatch timer source can be canceled or its timer parameters changed after creation.
I have been staring at my code for hours now so I thought I might try coming here for some fresh eyes. I needed to create a timer so I used the code below to do that. The first line is where I create the timer and the second part is my decrementTime method. This is in Objective C for an IOS app. This is my first time posting on StackOverflow (I usually find the answer I am looking for), so please let me know of any unwritten rules that I am not following.
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(decrementTime) userInfo:nil repeats:YES];
- (void)decrementTime{
self.timeLeft--;
}
I'll add here where I invalidate the first timer
-(IBAction)infoClick:(id)sender{
[_timer invalidate];
}
Then here is info message, where I create another timer
- (void)hideInfoMessage {
_secondTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(decrementTime) userInfo:nil repeats:YES];
}
clarification on my code: the order of events starts with my first code block (creating the timer). Then my second code block is called (invalidating the timer). then finally my third code block is called(making a new timer).
I know it is double incrementing because when I run the code I can visually see the timer double incrementing.
It's pretty easy to invoke the code that creates a timer twice. When you do that you actually have 2 timers running concurrently. Each one will decrement your value, so it will get decremented twice per second.
If you create a timer in your viewWillAppear method, for example, then you need to invalidate it in your viewWillDisappear method so you're sure you only have one running.
The same approach applies to other situations where you create a timer. You need to make sure you balance every call that creates a timer with a call that invalidates that timer.
If you use one of the scheduledTimer... methods, you can save a weak pointer to the timer. The run loop will retain it as long as it's running. When you invalidate it, the system run loop will release it and it will be deallocated. When that happens your weak pointer gets zeroed, so you don't even have to test it to see if it's valid/nil in your viewWillAppear method.
EDIT:
You need to instrument your code. In your infoClick method, is the variable _timer nil? What is it's address?
BTW, the target of an NSTimer is supposed to be a method that takes a single parameter, the timer itself. You should change your decrementTime method to look like this:
- (void) decrementTime: (NSTimer *) timer
{
NSLog(#"In method decrementTime, timer = %X", (unsigned long) timer)
self.timeLeft--;
}
Then look at your log and see if your decrementTime method is being called from 2 different timers (I would bet money that it is.)
You might also want to log the address of the timers you get back from your calls to scheduledTimerWithTimeInterval...
I have a method which displays a clock with seconds and the current time. This works fine except that this code will get called either half way through the current second or three quarters of the way through the current second depending on what time I open the app or run it. The method is called through the viewDidLoad method. When this happens my clock will be off up to almost 1 second. Is there any way to start my method when the next second start exactly? i.e. start it when the devices time is HH:MM:SS.000? Note: sorry if this is confusing with the excessive use of second and clock. I just mean I need to start my method at HH:MM:SS.000 (devices internal clock)
Using:
- (id)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)seconds
target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo
repeats:(BOOL)repeats
With an object of NSTimer is probably the way to go.
Add the logic found in this StackOverflow question/answers and you should be able to get it right on an exact second. (Use the logic there to create an NSDate object with resolution to 1 second, then use that date in the method I mentioned above).
NSTimer *yourTimer = [[NSTimer alloc] initWithFireDate:nowToTheSecond
interval:1.0 target:self selector:#selector(updateClock) userInfo:nil
repeats:YES];
[[NSRunLoop mainLoop] addTimer:yourTimer forMode:NSRunLoopCommonModes];
NSTimer objects are not exact. They depend on the app visiting the event loop frequently, and can vary by 50 MS or more (according to what I've read in the docs). If I remember correctly they try to "snap back" to the desired time interval rather than drifting, but any given firing will not be exact.
That said, I guess what I would do is to take the current NSDate, convert it to an NSTimeInterval, take the ceiling value (the next higher whole number) and start a one-time timer that will fire at that moment. Then in the handler for that timer, start a once-a-second timer. Something like this:
//Get the current date in seconds since there reference date.
NSTimeInterval nowInterval =[NSDate timeInervalSinceReferenceDate];
//Figure out the next even second time interval.
NSTimeInterval nextWholeSecond = ceil(nowInterval);
//Figure out the fractional time between now and the next even second
NSTimeInterval fractionUntilNextSecond = nextWholeSecond - nowInterval;
//Start a one-time timer that will go off at the next whole second.
NSTimer oneTimeTimer = [NSTimer timerWithTimeInterval: fractionUntilNextSecond
target: self
#selector: (startSecondTimer:)
userInfo: nil
repeats: NO];
And the startSecondTimer method:
- (void) startSecondTimer: (NSTimer *)timer;
{
//Start a new, repeating timer that fires once per second, on the second.
self.secondsTimer = [NSTimer timerWithTimeInterval: 1.0
target: self
#selector: (handleSecondTimer:)
userInfo: nil
repeats: YES];
}
You should still calculate the new time in each call to your handleSecondTimer: method rather than relying on the number of times you are called, because if the system gets really busy at the moment when it's supposed to call your timer and can't get to you, it might skip a call completely.
Disclaimer: I haven't tried this, but it should work. My only concern is edge cases. For example, when the next whole second is too close to now and the one-time timer can't fire fast enough. It might be safer to add a second to the fractionUntilNextSecond value, so the second hand doesn't start running for greeter than 1 second but less than 2 seconds.
I am currently writing an app for the iOS platform which communicates to a server through TCP. The problem is: I want to start an NSThread which runs into an infinite loop that checks that my
- (BOOL)connected
still returns true every 10 seconds, once the connect function has returned successful.
How can I make a timer that runs a function and checks its return value every 10 seconds?
Any help is appreciated, thanks in advance.
Why do not you use NSTimer to run a timer which will run every 10 seconds to check the status of connection?
NSTimer reference is given here. There are many static methods to create a timer. You can use for example:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
You can use it as
NSTimer * timer = [NSTimer timerWithTimeInterval:10 target:myobject selector:checkConnectionStatus userInfo:nil repeats:YES];
where myobject will be an Objective-C object and its method -checkConnectionStatus or which method you like will check the status of the connection.
You should checkout the Reachability sample code from Apple. I don't know much about TCP, but what you are describing sounds like it is going to burn through the battery quickly. Unless TCP works without waking the radios off course.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have a method that starts my game (startGame). I call it in viewDidLoad ([self startGame];). The problem is, that the start game method checks to see if an object has moved before it starts the game. The setup I have right now only calls the method once and doesn't check it again which means my game never starts. Any ideas on how I can continue checking the method UNTIL the game starts, and then stop checking it?
Thanks. Happy 2012
there are a few things you can do.. one of the easiest would be to use a NSTimer. so in your viedDidLoad method you could do:
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(checkObjectMoved:) userInfo:NULL repeats:YES]; // check every 1 second
then you'd have:
- (void)checkObjectMoved:(NSTimer *)timer {
if object has NOT moved return;
[timer invalidate];
[self startGame];
}