NSTimer Not Invalidated - objective-c

I am implementing a timeout for the RKObjectManager. My code snippet is as follows:
-(void)getObjects
{
RKObjectManager *sharedManager = [RKObjectManager sharedManager];
[self showLoading];
[sharedManager loadObjectsAtResourcePath:self.resourcePath delegate:self];
// Setting timeout here. goto failure
self.nTimer = [NSTimer scheduledTimerWithTimeInterval:TIMEOUT_INTERVAL target:self selector:#selector(didEncounterError) userInfo:nil repeats:NO];
}
- (void) didEncounterError
{
[self hideLoading];
[self standardErrorHandling];
//invalidate timer, this is done to ensure that if error occurs before timer expiry time, the error will not show again when timer is up (ISSUE HERE)
[self.nTimer invalidate];
}
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects
{
....
//invalidate timer if load is successful (no issue here)
[self.nTimer invalidate];
}
- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError*)error
{
....
//trigger encounter error method
[self didEncounterError];
}
In the above implementation, I will always invalidate the timer in the "encounter error" method. This is to mitigate cases whereby error has occurred before the timer expires. I want to invalidate the timer in this case to prevent the error message from popping up again.
However, I am still getting the error message a second time after the error has occurred (before timer expires). It seems like the invalidation in the "encounter error" method didnt work. Any advise on what is wrong with my code?

The timer invalidation should happen on the thread where it is scheduled, in above case it is called on another thread (call backs). Can you have a method that does this invalidation and call that method using 'performSelectorOnMainThread' at your call back methods?

Related

How to skip scheduled call to a method when an asynchronous task is ongoing on that method itself?

Suppose I have a method that does some asynchronous tasks. Let's say it refreshes user's access permission and it may take several minutes depending on the internet connection speed or whatever.
I have to call this method periodically (i.e. scheduled call using NSTimer's method scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:)
-(void)refreshPermission {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do something that takes a few minutes
});
}
Now I call this method as timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(refreshPermission) userInfo:nil repeats:YES];. That is, this call is fired every 10 seconds.
What I need to do is, I need to somehow skip one (or, more than one)
scheduled call to this method if something is happening inside that
asynchronous block (Let's say, user's access permission hasn't been
updated).
But once the block is done (that is, user's access permission has been updated), scheduled call with timer should resume.
Any idea or any sample on how to accomplish this??
I think you can do it by using a Bool variable. You can declare Bool variable globally and by using its state you can manage your task in function call.
In method refreshPermission
-(void)refreshPermission {
if(!isExecuting){
isExecuting = YES;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Perform your tasks
isExecuting = NO;
}
}
}
I've come up with this approach. Got the idea from #Sunny's answer.
It worked for me. But any suggestion regarding this implementation is appreciated.
-(void)refresh {
NSLog(#"Refresh called");
NSLock *theLock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"Async task assigned");
if(!isExecuting){
[theLock lock];
isExecuting = YES;
[theLock unlock];
// Perform your tasks
NSLog(#"Async task started");
[NSThread sleepForTimeInterval: 13.0]; // for testing purpose
NSLog(#"Async task completed");
[theLock lock];
isExecuting = NO;
[theLock unlock];
}
});
}
Here isExecuting is an instance variable of the containing class. And it was set to isExecuting = NO; before setting the actual scheduled timer for calling the method periodically.
Here I used NSLock for the assurance that no other thread can change the value of isExecuting while a thread is in execution of it's task. I added this locking because every time -(void)refresh method is invoked, there is a possibility that multiple threads become eligible for the execution and changing value of isExecuting. So it's better to make it thread save when changing value of shared variable.

Can we update the variables that are manipulated by NSTimer?

I am using NSTimer that updates 2 different values secondsLeft and totalSecondsLeft. I don't have to change secondsLeft but I have to add more seconds to totalSecondsLeft when an action is performed.
Whenever I assign a value to totalSecondsLeft the timer still updating and printing the previous value. I saw different solutions and tried invalidating the timer and start it again after updating the totalSecondsLeft value but not working.
So I ended up creating 2 different instances of the timer with same variables but printing different values i.e, secondsLeft is same in both cases but totalSecondsLeft is different in both cases.
I tried many solutions and added timer instance to GCD main_queue using functions and invalidating the timer from the same queue still not getting working solution.
Here is my code:
[self settime];
-(void)settime
{
dispatch_async(dispatch_get_main_queue(), ^
{
timer=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(Countdown) userInfo:nil repeats:YES];
});
}
-(void) Countdown
{
secondsLeft=secondsLeft-1;
totalSecondsLeft=totalSecondsLeft-1;
NSLog(#"secondsLeft : %i and totalSecondsLeft : %i",secondsLeft,totalSecondsLeft);
}
-(void)otherMethod
{
dispatch_async(dispatch_get_main_queue(), ^
{
timer=nil;
[timer invalidate];
});
[self settime];
}
Help me to figure out. Thanks in advance

Invalidating a NSTimer from within its invocation method

I have scheduled a timer using [NSTimer scheduledTimerWithTimeInterval:target:selector:userInfo:] and want to invalidate it at some point when it fires.
- (id)init
{
[NSTimer scheduledTimerWithInterval:1 target:self selector:#selector(fired:) userInfo:nil repeats:YES];
}
- (void)fired:(NSTimer *)timer
{
if (someCondition) {
[timer invalidate];
}
}
Is this allowed? The documentation states
You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.
If this is not a proper way to accomplish this task: What is the proper way?
Calling [timer invalidate] from within the fire method is just fine, that code will be executed in the same thread as the one used when you created the timer.
The Apple Doc you quoted only warns you that if you create a separate thread and invalidate the timer from it, then, and only then, should expect unpredictable behaviour.
Ex.
// Create the background queue
dispatch_queue_t queue = dispatch_queue_create("do not do this", NULL);
// Start work in new thread
dispatch_async(queue, ^ {
// !! do not do this !!
if (someCondition) {
[yourTimer invalidate];
}
// or this
[self fire:yourTimer];
});
// won’t actually go away until queue is empty
dispatch_release(queue);
It is fine to invalidate it from the fired method, as the fired method is on the same thread the timer was scheduled on:
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
Creates and returns a new NSTimer object and schedules it on the current run loop in the default mode.

Simple NSThread or NSTimer

I want to run certain background tasks.
Scenario: I would like a button to activate a thread or timer, and then have the thread/timer to start repeating every second returning a NSRunInformationalAlertPanel to the user with data.
This is what I have for my timer:
-(void)workerThread:(NSTimer*) theTimer {
if(intNumberOfTicks > 0)
{
NSRunInformationalAlertPanel(#"The Serial", [NSString stringWithFormat:#"%d", intNumberOfTicks], #"OK", nil, nil);
//[txtTimeMinutes setStringValue:[NSString stringWithFormat:#"%d", intNumberOfTicks]];
intNumberOfTicks--;
}
else {
[timer invalidate];
}
}
And for starting the method...
intNumberOfTicks = 5;
timer = [[NSTimer scheduledTimerWithTimeInterval:1 target: self selector:#selector(workerThread:) userInfo:self repeats:true] retain];
// Or for threading...
///[NSThread detachNewThreadSelector:#selector(workerThread) toTarget:self withObject:nil];
Can anyone help me implement what I need, maybe providing the most basic examples for a NSThread or NSTimer. I have looked at the Apple Dev Refrences but no luck.
Using NSTimer will execute the selector in the same thread as the one which instantiated and invoked it.
If your task must be carried out in a background thread try calling performSelectorInBackground:withObject:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/performSelectorInBackground:withObject:
From that background thread you can use a scheduled timer the way that you described above.

Objective-C: Blocking a Thread until an NSTimer has completed (iOS)

I've been searching for and attempting to program for myself, an answer to this question.
I've got a secondary thread running inside my mainView controller which is then running a timer which counts down to 0.
Whilst this timer is running the secondary thread which initiated the timer should be paused/blocked whatever.
When the timer reaches 0 the secondary thread should continue.
I've Experimented with both NSCondition and NSConditionLock with no avail, so id ideally like solutions that solve my problem with code, or point me to a guide on how to solve this. Not ones that simply state "Use X".
- (void)bettingInit {
bettingThread = [[NSThread alloc] initWithTarget:self selector:#selector(betting) object:nil];
[bettingThread start];
}
- (void)betting {
NSLog(#"betting Started");
for (int x = 0; x < [dealerNormalise count]; x++){
NSNumber *currSeat = [dealerNormalise objectAtIndex:x];
int currSeatint = [currSeat intValue];
NSString *currPlayerAction = [self getSeatInfo:currSeatint objectName:#"PlayerAction"];
if (currPlayerAction != #"FOLD"){
if (currPlayerAction == #"NULL"){
[inactivitySeconds removeAllObjects];
NSNumber *inactivitySecondsNumber = [NSNumber numberWithInt:10];
runLoop = [NSRunLoop currentRunLoop];
betLooper = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(betLoop) userInfo:nil repeats:YES];
[runLoop addTimer:[betLooper retain] forMode:NSDefaultRunLoopMode];
[runLoop run];
// This Thread needs to pause here, and wait for some input from the other thread, then continue on through the for loop
NSLog(#"Test");
}
}
}
}
- (void)threadKiller {
[betLooper invalidate];
//The input telling the thread to continue can alternatively come from here
return;
}
- (void)betLoop {
NSLog(#"BetLoop Started");
NSNumber *currentSeconds = [inactivitySeconds objectAtIndex:0];
int currentSecondsint = [currentSeconds intValue];
int newSecondsint = currentSecondsint - 1;
NSNumber *newSeconds = [NSNumber numberWithInt:newSecondsint];
[inactivitySeconds replaceObjectAtIndex:0 withObject:newSeconds];
inacTimer.text = [NSString stringWithFormat:#"Time: %d",newSecondsint];
if (newSecondsint == 0){
[self performSelector:#selector(threadKiller) onThread:bettingThread withObject:nil waitUntilDone:NO];
// The input going to the thread to continue should ideally come from here, or within the threadKiller void above
}
}
You can't run a timer on a thread and sleep the thread at the same time. You may want to reconsider whether you need a thread at all.
There's a few things that need to be pointed out here. First, when you schedule your timer:
betLooper = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(betLoop:)
userInfo:nil
repeats:YES];
it's added to and retained by the current run loop by that method, so you don't need to do that manually. Just [myRunLoop run]. Your timer's selector argument is also invalid -- a timer's "target method" needs to look like this:
- (void)timerFireMethod:(NSTimer *)tim;
This also means that you don't need to retain the timer if all you want to do is invalidate it, since you will have a reference to it from inside that method.
Second, it's not clear what you mean by "this thread needs to sleep to wait for input". When you schedule that timer, the method (betLoop) is called on the same thread. If you were to sleep the thread, the timer would stop too.
You seem to be a little mixed up regarding methods/threads. The method betting is running on your thread. It is not itself a thread, and it's possible to call other methods from betting that will also be on that thread. If you want a method to wait until another method has completed, you simply call the second method inside the first:
- (void)doSomethingThenWaitForAnotherMethodBeforeDoingOtherStuff {
// Do stuff...
[self methodWhichINeedToWaitFor];
// Continue...
}
I think you just want to let betting return; the run loop will keep the thread running, and as I said, the other methods you call from methods on the thread are also on the thread. Then, when you've done the countdown, call another method to do whatever work needs to be done (you can also invalidate the timer inside betLoop:), and finalize the thread:
- (void)takeCareOfBusiness {
// Do the things you were going to do in `betting`
// Make sure the run loop stops; invalidating the timer doesn't guarantee this
CFRunLoopStop(CFRunLoopGetCurrent());
return; // Thread ends now because it's not doing anything.
}
Finally, since the timer's method is on the same thread, you don't need to use performSelector:onThread:...; just call the method normally.
You should take a look at the Threading Programming Guide.
Also, don't forget to release the bettingThread object that you created.
NSThread has a class method + (void)sleepForTimeInterval:(NSTimeInterval)ti. Have a look at this :).
NSThread Class Reference