I'm trying to do something quite simple : Stopping a parse.com query after a few seconds, with an NSTimer. I've read after some reasearch it's a good "trick" to use.
Here is how I create my timer :
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:6.0
target:self
selector:#selector(stopRetries:)
userInfo:#{#"query":query}
repeats:NO];
Because i'm running this on a background thread (and outside a viewcontroller class), the timer is inside a dispatch_sync(dispatch_get_main_queue());
But whatever I do, I cannot stop the query, because [query cancel] doesn't do anything. I can't pass it in the userInfo of the timer. Breakpoints show it has an address and is "there" but it looks like junk inside.
What can I be doing wrong and what should I be doing instead?
My main goal is to make the parse.com query stop faster than 30 seconds, and warn the user with an alert.
You can Try this [self performSelector:#selector(abc) withObject:nil afterDelay:6.0];
or
You can Invalidate the timer
Related
I am running into a situation where I'm not able to properly handle NSTimer.
In my app, I've an option of user chats (I'm not using XMPP because of a low budget project, but the chat is working through API calls ONLY). I've scheduled a timer at a time interval of 15 seconds. If any new chats available I'll get it and will update chat view.
Here's the working scenario:
As this is a UITabbar based app, a user will come to "Chat" tab.
A User will have a list of persons with whom he can chat.
A User will select any of a user – will push to Chat Screen.
Where all locally saved chats will be visible and an API call will be made for new chats, on success (or error) of API call, a timer will be scheduled to sync chats at a time interval of 15 seconds.
If a user goes back (pops), in viewDidDisappear: method, I'm invalidating the (running) timer.
In my Unit testing, if I'll continuously push & pop to/from Chat screen, there'll be multiple instances of that timer will get scheduled. I suspect, this is WRONG.
I'm not sure what I'm doing is correct or not though I need your help to understand the right and the correct way to get my functionality done. I guess here there's no need of the code for above explanation.
First of all, why are you not exploring option of push notification? Polling server every 15 second is a bad design :-(.
Second, when it comes to NSTimer it is important to start and stop them from the same thread. I would advise you encapsulate your timer start/stop code in below block always ensuring you deal on main thread with your timer.
dispatch_async(dispatch_get_main_queue(), ^{
});
This is the way o usually work with NSTimer. Hope it helps.
#implementation MainViewController{
NSTimer *myTimer
}
- (void)startTimer{
//Prevents multiple timers at the same time.
[myTimer invalidate];
myTimer = [NSTimer scheduledTimerWithTimeInterval:5.0f target:self selector:#selector(update) userInfo:nil repeats:YES];
}
- (void)update
{
//Stops the timer if the view in not on the screen
if (!(self.isViewLoaded && self.view.window)) {
[myTimer invalidate];
}
}
#end
When I use NSTimer to call a function on certain time interval as follows:
NSTimer *myTimer =[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(func1) userInfo:nil repeats:YES];
does a background thread gets invoked so as to handle the timer to call func1 every 2 minutes ?
How does the program control flows before & after the NSTimer code section? Does these code segments run on main thread ?
The timer will be called on the main thread (or whatever thread the timer is scheduled on) and this functionality is handled by the run loop.
You can test this with:
- (void)func1
{
NSLog(#"Ping! On main thread=%#", [NSThread isMainThread] ? #"Yes" : #"No");
}
The timer's attached to what's called a run loop. The run loop basically controls a thread's operations (in this case, the main thread). It sleeps the thread until awoken by some sort of trigger, e.g. user input, a timer going off, a system message. Once triggered it dispatches the triggering event to the appropriate place, in this case it will invoke your 'func1'. Once func1 returns back to the run loop, it will look for any other input/triggers, and if there are none, sleeps the thread again.
NSTimers are scheduled on the current thread's run loop. So, whichever thread you call the timer on, callback would happen on that thread only.
For instance, below code fires timer of main thread and ensures func1 is called on main thread.
NSTimer *myTimer =[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(func1) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:myTimer forMode:NSRunLoopCommonModes];
You can utilize NSOperation to enable timer & call back on background thread. You need to schedule the timer on [NSRunLoop currentRunLoop]. Of-course GCD is another alternative for timers.
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 two separate timers on for recording data at 0.25 seconds and other for recording location details at 1 sec as below
self.hardBrakingTimer = [NSTimer scheduledTimerWithTimeInterval:0.25
target:self
selector:#selector(timerFired:)
userInfo:nil
repeats:YES];
self.locationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(locationTimerFired:)
userInfo:nil
repeats:YES];
But sometimes these timers are not calling at given time intervals.
You are scheduling a timer in a run loop in the default mode. A Run Loop is basically a queue, scheduling tasks on a certain thread. The exact execution time of your timer depends whether there are already other tasks executing in the same run loop.
It's also possible, that another run loop runs "on top" or modifies the "mode" such that other "tasks" are deferred (e.g a user scrolls). You can read more about run loops and "modes" and their surprising behavior in the official documentation: Anatomy of a Run Loop.
You are also likely not getting a precise timer with NSTimer anyway, since - as already pointed out by #Cyrille - there is that "Timer coalescing" feature of iOS and OSX: Timer Coalescing. You can however implement a quite precise timer using dispatch lib, see a code sample of mine: RXTimer
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.