Command line tool not terminating after adding a timer - ios

Im writing a little command line tool that use AFHTTPNetworkng to pull some data from a server and display it. However because AFHTTPNetwotking uses an asynchronous call back, the application exits prior to getting the response from the server. I there for added
NSRunLoop *run = [NSRunLoop currentRunLoop];
_timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:#selector(check) userInfo:nil repeats:YES];
[run addTimer:_timer forMode:NSDefaultRunLoopMode];
[run runUntilDate:[NSDate distantFuture]];
this timer calls a check method that checks to see if a BOOL has been set to yes (This happens in the call back). If it has it invalidates the timer.
- (void)check{
NSLog(#"checking");
if (test == YES) {
[_timer invalidate];
}
return;
}
This code works fine but now the app does not terminate, suggesting that the runloop is still running. I could use exit() to kill the app but this does not seem acceptable as this may cause a memory leak.

Related

NSTimer inside global queue is not called when the app is in the background

I want to run a selector using NSTimer which contains some network calls and some other tasks. I want to do that on global queue.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTimer * myTimer=[[NSTimer alloc]init];
myTimer = [NSTimer timerWithTimeInterval:10*60 target:self selector:#selector(syncGroupAutomatically) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
});
-(void)syncGroupAutomatically
{
NSLog(#"Some Network calls and some other things\n");
}
If I run this code, it works fine when app is in foreground, but as soon as I press the home button it stops calling the syncGroupAutomatically method.
If anybody having any idea how to run this NSTimer even when app is in the background. Please help me.

NSTimer while in background mode is working -- but shouldn't it NOT be working?

I've opted in to background location updates in an App I'm working on.
In my LocationManager class, I've got a method that looks like this:
- (void)beginUpdateTimer
{
[self.updateTimer invalidate];
self.updateTimer = [NSTimer timerWithTimeInterval:ForceUpdateDuration
target:self
selector:#selector(updateWithLastKnownLocation)
userInfo:nil
repeats:NO];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addTimer:_updateTimer forMode:NSRunLoopCommonModes];
}
The method -updateWithLastKnownLocation potentially calls beginUpdateTimer again.
In testing my app, I've discovered that the timer continues to fire upon the app moving in to the background, so long as I've enabled background location updates. Shouldn't this NOT be happening though? Can I rely on this?
Thanks!
Yes, you can rely on it, provided the timer was running at the time you went into the background.

Unable to timeout NSOperation with NSTimer

I am trying to timeout my NSOperation with a NSTimer but my timer is not getting fired. Please see below the code I have written inside my class which is sub classing NSOperation.
- (void)start {
// Start the timer for Time out before the ping activity starts
self.timeOutTriggerTimmer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(cancelTheOperation) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:self.timeOutTriggerTimmer forMode:NSDefaultRunLoopMode];
// Check for cancellation
if ([self isCancelled]) {
[self completeOperation];
return;
}
// Executing
[self willChangeValueForKey:#"isExecuting"];
executing = YES;
[self didChangeValueForKey:#"isExecuting"];
// Begin
[self beginOperation];
}
It's easiest to just add the timer to the main run loop, not the current run loop:
[[NSRunLoop mainRunLoop] addTimer:self.timeOutTriggerTimmer forMode:NSDefaultRunLoopMode];
Alternatively, you can keep your timer as it is (scheduled on the current run loop), but then you have to keep the runloop alive, perhaps adding something like the following to the end of the start method (note, Apple recommends this rather than the run method):
while ([self isExecuting] && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
But this (like the run method you contemplated) effectively keep the start method running until execution is done (making what appears to be a concurrent operation to behave more like a non-concurrent operation).
I suspect you're doing this already, but just in case, make sure to invalidate your timer when you complete your operation, or else the timer will retain the operation until the timer fires, unnecessarily delaying the freeing of the operation's resources (and calling the cancelTheOperation even though the operation may well already be done).
I found the issue. I need to put the below statement to have it executed.
[[NSRunLoop currentRunLoop] run];

NSTimer periodic task doesn't get called while scrolling

I have an NSTimer
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(periodicTimer)
userInfo:nil
repeats:YES];
which does
- (void)periodicTimer
{
NSLog(#"Bang!");
if (timerStart != nil)
[timerLabel setText:[[NSDate date] timeDifference:timerStart]];
}
The problem is that while scrolling a tableview (or doing other tasks) the label doesn't get updated, furthermore, "Bang!" doesn't appear, so I supposed the method doesn't get called.
My question is how to update the label periodically even when the user is playing around with the app interface.
You'll need to add your timer to the UITrackingRunLoopMode to make sure your timer also fires during scrolling.
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
NSTimer *timer = [NSTimer timerWithTimeInterval:0.1 target:self selector:#selector(myTimerAction:) userInfo:nil repeats:YES];
[runloop addTimer:timer forMode:NSRunLoopCommonModes];
[runloop addTimer:timer forMode:UITrackingRunLoopMode];
From:
https://stackoverflow.com/a/1997018/474896
Not sure about this one, but my first guess would be that the main thread on which the interface is being rendered your timer just doesn't get a chance to do anything while its updating the interface.
You could create a new thread with a new run loop for your timer, but that is a bit of an ugly solution maybe. What functionality in your app are you trying to achieve? Maybe we can advise a better strategy than using a timer.

The selector of my NSTimer does not run. Why?

My code is:
-(void) timerRun{...}
-(void) createTimer
{
NSTimer *timer;
timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:#selector(timerRun)
userInfo:nil
repeats:YES];
}
viewDidLoad
{
[NSThread detachNewThreadSelector:#selector(createTimmer)
toTarget:self withObject:nil];
...
}
When I debug, the method createTimer runs ok, but the method does timerRun not run?
Just creating a timer doesn't start it running. You need to both create it and schedule it.
You're actually going to have to do slightly more work than that if you want it to run on a background thread. NSTimers attach to NSRunloops, which are the Cocoa form of an event loop. Each NSThread inherently has a a run loop but you have to tell it to run explicitly.
A run loop with a timer attached can run itself indefinitely but you probably don't want it to because it won't be managing autorelease pools for you.
So, in summary, you probably want to (i) create the timer; (ii) attach it to that thread's run loop; (iii) enter a loop that creates an autorelease pool, runs the run loop for a bit and then drains the autorelease pool.
Code will probably look like:
// create timer
timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:#selector(timerRun)
userInfo:nil
repeats:YES];
// attach the timer to this thread's run loop
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// pump the run loop until someone tells us to stop
while(!someQuitCondition)
{
// create a autorelease pool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// allow the run loop to run for, arbitrarily, 2 seconds
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
// drain the pool
[pool drain];
}
// clean up after the timer
[timer invalidate];
You have to schedule a timer for it to run. They get attached to a run loop, which in turn updates the timer as necessary.
You can either change createTimer to
[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timerRun)
userInfo:nil
repeats:YES];
or add
[[NSRunLoop currentRunLoop] addTimer:timer forModes:NSRunLoopCommonModes];
The method signature that you use in scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: must have an argument for the NSTimer as it passes itself as an argument.
You should change your message signature to:
(void)timerRun:(NSTimer *)timer;
You don't need to do anything with the argument, but it should be there. Also in createTimer the selector will become #selector(timerRun:) as it now accepts an argument:
timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:#selector(timerRun:)
userInfo:nil
repeats:YES];

Resources