how to stop a timer triggered runloop? - iphone

if i set up a runloop like that:
NSRunloop* loop = [NSRunloop currentRunLoop];
[runLoop addTimer:anyTimer forMode:NSDefaultRunLoopMode];
can i stop it again ?
or is the only way to ignore the notification i use to trigger the further action ?
ok, i give an example for the problem:
-(void)blinkeffekt:(double)pollingTime{
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
if (pollingTime != 0) {
NSTimeInterval interval =(double)pollingTime / 1000;
NSTimer timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:#selector(polling) userInfo:nil repeats:YES];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
}
else {
[timer invalidate];
}
}
surely, here are several errors - no question. but i think, it shows my problem. and this is not really solvable with the answers until now.
i need to run a timer and stop it later. and ideally out of another function in the class.
but then i cannot access "timer" anymore and runloop doesnt allow to figure out, if such a "message" is available.
and it would be extremely ineffective, if i would register for each calling of the function a new timer.

You need to send a invalidate message to the timer to remove it from the RunLoop.
See Doc
[anyTimer invalidate];

Related

NSRunLoop is calling my selector after long Delay

Problem statement : we have one secondary thread in which we are doing all backend processing.In this secondary thread we have created a separate NSRunLoop to run. we create and use timer in this runloop
NSAutoreleasePool *myPool = [[NSAutoreleasePool alloc] init];
NSRunLoop * threadRL = [NSRunLoop currentRunLoop];
[threadRL addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[threadRL run];
[myPool release];
Every thing is run fine except one of the call to a selector is taking almost 10 second to execute and this happens randomly not every time.
[myclass performSelector:#selector(func) onThread:myThread withObject:nil waitUntilDone:NO];
I tried this also with no difference.
[myclass performSelector:#selector(func) onThread:myThread withObject:nil waitUntilDone:NO modes:[NSArray arrayWithObjects: NSDefaultRunLoopMode, NSRunLoopCommonModes,nil]];
I am not doing any task in func which could possibly take this much time,
What I am thinking is that it might be possible that runloop is in different mode.
Is there a way to make fund be executed in highest priority i.e what ever is being executed in runloop be interrupted or something like that.
You could create a NSTimer and set the firedate to now. Please see the sample code below.
NSTimer *timer = [[NSTimer alloc] initWithFireDate:now
interval:.01
target:myClass
selector:#selector(funct)
userInfo:nil
repeats:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[runLoop run];
I am answering my own question. My runloop was waiting on NSInputStream:read when there was no byte to read. So the func was being queued on in runloop source but as the thread is waiting in on socket read nothing was getting executed.
I got to know this when I hooked the runloop and saw it is getting stuck there.

NSTimer not running

I've got a program in which I want to code a timer which checks wether the user is idle or not. For that I wrote following code:
if (!idleTimer) {
NSLog(#"make");
idleTimer=[[NSTimer alloc]initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:10.0]interval:10 target:self selector:#selector(idleTimerExceeded) userInfo:nil repeats:NO];
NSLog(#"madetimer with: %f, %#", idleTimer.timeInterval, idleTimer.fireDate);
}else {
NSLog(#"no reset timer: %f", idleTimer.timeInterval);
if (fabs([idleTimer.fireDate timeIntervalSinceNow]) < 9) {
NSLog(#"reset");
[idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}
}
But somehow my logs show that the interval is always 0.0000 . Which means that something is wrong here. Can anybody help me?
ive never seen a timer look so complicated. try this:
write a method that checks if the user is idle (lets say idleChecker)
then make the timer repeatable and calls that method idleChecker
[NSTimer scheduledTimerWithTimeInterval:.5 target:self selector:#selector(idleChecker) userInfo:nil repeats:YES];
remember to declare the idleChecker method in the .h file
take note if u want to stop the timer event then you need to maintain a reference to it
NSTimer aTimer = [NSTimer scheduledTimerWithTimeInterval:.5 target:self selector:#selector(idleChecker) userInfo:nil repeats:YES];
then to stop it
[aTimer invalidate];
as for checking if its running i would just stick a nslog message in there stating something like "check for idle user"

NSTimer to update label

self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimerDisplay) userInfo:nil repeats:YES];
[runLoop addTimer:self.timer forMode:NSRunLoopCommonModes];
This code-snippet is copied from my viewDidLoad method, so it is runned from the main-thread. All it do is to call a method to update a label.
I thought I need to have a own thread for doing this, but after getting help on this at SO I figured out that I did not.
However, I do not understand the NSRunLoopCommonModes. Why does it work?
AND the timer updates the label which is a "digital counter" which is on the same screen as a tableview so it CAN'T stop the timer even if the user holds the screen.
Thanks.
A NSRunLoop can run in different input modes. The mode defines which events are handled by the current runloop.
e.g.: If the current runloop is in event tracking mode, it only handles modal event loops. (e.g. dragging a NSScrollBar or a NSSlider on the Mac)
If you add your NSTimer only for NSDefaultRunLoopMode it won't fire if something is causing a modal event loop. (Details in Apple's documentation)
NSRunLoopCommonModes is an "alias" for multiple modes so that you don't have to do:
[[NSRunLoop currentRunLoop] addTimer:mRenderDurationTimer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:mRenderDurationTimer forMode:NSModalPanelRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:mRenderDurationTimer forMode:NSEventTrackingRunLoopMode];
I don't think you have to have this line at all, the first line is enough... I use PSYBlockTimer in my code which derives from the SDK method you use, but instead of a selector calls a block :
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:NO usingBlock:^ (NSTimer *t)
{
// stuff that will get executed in a second
}];
If you add your time to an instance of NSRunLoop under another thread, you need a while loop for this NSRunLoop of the thread. It looks like following:
do {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
} while (bDone);
Normally, I add the above code on my thread main function, and when thing is done, the thread go die and the autorelease pool of the thread will be released.

How do I check if an NSTimer is running?

I have an IBAction where upon the button being pressed, it creates an unscheduled timer. Then, if that same timer has already started, //do something, else start the timer which was created.
Here is what I have so far:
- (IBAction)button1Press {
NSMethodSignature *sgn = [self methodSignatureForSelector:#selector(onTick:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sgn];
[inv setTarget: self];
[inv setSelector:#selector(onTick:)];
NSTimer *tapTimer = [NSTimer timerWithTimeInterval: 1.0
invocation:inv
repeats:NO];
if (/*tapTimer is running*/) {//do something
}else{
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer: tapTimer forMode: NSDefaultRunLoopMode];
}
}
My problem is what to put as the condition. If I put tapTimer isValid or != nil then it always returns true, because tapTimer is already declared. I do not want to invalidate or nil out the timer because the main purpose of the button is to only do the action if the button is pressed twice in a time interval of 1 second.
If there is a completely different approach to do what I want then please do tell!
Thanks loads!
From what I understand so far, I see that you are attempting to check whether tapTimer is running or not. I have one suggestion. Use a variable for indicating whether you are having tapTimer running or not. When you run the timer, you change this variable to true, and when the timer's time hits 0 and invoke the method you have selected, you change this variable to false in this method.
Does this help?
I would recommend using a nil check to determine if your timer is running or not.
...
//Define _tapTimer in .h
if (_tapTimer) {//do something
}
else{
_tapTimer = [[NSTimer timerWithTimeInterval: 1.0
invocation:inv
repeats:NO] retain];
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer: _tapTimer forMode: NSDefaultRunLoopMode];
}
...
-(void)timerFired:(NSTimer*)timer
{
if(timer == _tapTimer)
{
//Handle timerfired
[_tapTimer release], _tapTimer = nil;
}
}
You can query the RunLoop to know if there is such a timer inside the loop
CFRunLoopRef loopRef = [[runner currentRunLoop] getCFRunLoop];
Boolean timerAdded = CFRunLoopContainsTimer(loopRef, (CFRunLoopTimerRef)timer ,kCFRunLoopDefaultMode)
if (timerAdded)
{
...
}
but, I haven't tested that code yet

NStimer -- what am I doing wrong here?

I've been using an NSTimer successfully, but am now having trouble with it. Undoubtably something stupid. Appreciate another set of eyes. Running the debugger, I see that applicationDidFinishLaunching is called, but trigger is never called.
-(void) trigger:(NSTimer *) theTimer{
NSLog(#"timer fired");
}
- (void)applicationDidFinishLaunching:(UIApplication *)application {
nst = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(trigger) userInfo:nil repeats:YES];
[window makeKeyAndVisible];
}
The selector must have the following signature:
- (void)timerFireMethod:(NSTimer*)theTimer
so you need
#selector(trigger:)
--edit--
Maybe you are doing this somewhere else, but in the code you included you do not actually start the timer. You have to add it to a NSRunLoop before it can trigger any events at all.
[[NSRunLoop currentRunLoop] addTimer:nst forMode:NSDefaultRunLoopMode];
If I read the examples correctly. I've only used the one the init method that automatically adds it to the current NSRunLoop. You really should look at the developer docs that someone included in the comments to my post.
Two things:
1) as others say, the method should have the following signature..
-(void) trigger:(NSTimer *) theTimer;
and you make the timer thus:
nst = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(trigger:) userInfo:nil repeats:YES];
2) merely creating the timer does not run it. As the documentation says:
You must add the new timer to a run
loop, using addTimer:forMode:. Then,
after seconds have elapsed, the timer
fires, invoking invocation. (If the
timer is configured to repeat, there
is no need to subsequently re-add the
timer to the run loop.)
Here's a piece of real functioning code that you can model after. The timer creation is the same as yours, but it also adds it to runloop the right way.
[[NSRunLoop currentRunLoop] addTimer:
[NSTimer timerWithTimeInterval:0.1
target:self
selector:#selector(someSelector:)
userInfo:nil
repeats:NO]
forMode:NSDefaultRunLoopMode];
The selector you're giving the timer, trigger, indicates that it should call a method that takes no parameter. Either change your timer-fired method to
- (void)trigger
{
// look at me, I don't take any parameters
NSLog(#"timer fired");
}
or change your initial timer call to use #selector(trigger:).
Your problem is due to the fact that timerWithTimeInterval:target:selector:userInfo:repeats: creates a timer but does not schedule it on the run loop, you have to do it yourself.
However, you may as well use this method which creates the timer and schedules it on the run loop: scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
I had a problem when starting timer in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { not in main thread.
dispatch_async(dispatch_get_main_queue(), ^{
[self startScheduledTimer];
});