I need to perform certain tasks (many tasks) at different intervals. How can i implement it with single NStimer instance as individual timers will create overhead?
I am doing this exact thing.
in my appdelegate i have a method for the timer and threads (i've removed the threads for simplicity):
-(void)startupThreadsAndTimers{
//updatetime
timer = [NSTimer scheduledTimerWithTimeInterval:(1)
target:self
selector:#selector(updateTime)
userInfo:nil
repeats:YES];
}
and i have a method that the timer triggers
-(void)updateTime{
[[NSNotificationCenter defaultCenter] postNotificationName: #"PULSE" object: nil];
}
in the "awakefromnib" method of the appdelgate class i have this:
[self startupThreads];
Now in the veiwdidload method on the viewcontrollers that need to subscribe to this (to update a clock) i subscribe to the notification centre:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateTimeLeft) name:#"PULSE" object:nil];
And i have a method called "updateTimeLeft"
This works really well but i will revisit it to use threads for a bit of performance at a later time.
To have things trigger at different intervals you could change these selectors to parse a time value and have the subscribing view controllers use a modulus to decide what to do.
i.e.
switch (timerValue % 15){
case 1:
[self someMethod];
break;
case 2:
[self someOtherMethod];
break;
}
Hope this helps :)
You can use NSNotificationCenter to define a notification and at particular time it dispatches a notification so other class can be notified and do some task accordingly...
refer this tutorial...
I had to use this in my iPhone App, this is what I did:
#define kUpdateInterval(x) (iPerformanceTime % x == 0)
int iPerformanceTime;
-(void) mPerformanceGameUpdates {
if (iPerformanceTime == 0) { //Initialization
}
if (kUpdateInterval(1)) { //Every 1sec
}
if (kUpdateInterval(2)) { //Every 2sec
}
if (kUpdateInterval(5)) { //Every 5sec
}
if (kUpdateInterval(10)) { //Every 10sec
}
if (kUpdateInterval(15)) { //Every 15sec
}
if (kUpdateInterval(20)) { //Every 20sec
}
if (kUpdateInterval(25)) { //Every 25sec
}
iPerformanceTime ++;
}
//The method below helps you out a lot because it makes sure that your timer
//doesn't start again when it already has, making it screw up your game intervals
-(void) mPerformanceTmrGameUpdatesLoopShouldRun:(BOOL)bGameUpdatesShouldRun {
if (bGameUpdatesShouldRun == TRUE) {
if (bPerformanceGameUpdatesRunning == FALSE) {
tmrPerformanceGameUpdates = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(mPerformanceGameUpdates) userInfo:nil repeats:YES];
bPerformanceGameUpdatesRunning = TRUE;
}
} else if (bGameUpdatesShouldRun == FALSE) {
if (bPerformanceGameUpdatesRunning == TRUE) {
[tmrPerformanceGameUpdates invalidate];
bPerformanceGameUpdatesRunning = FALSE;
}
}
}
-(void) viewDidLoad {
[self mPerformanceTmrGameUpdatesLoopShouldRun:TRUE];
//TRUE makes it run, FALSE makes it stop
}
Just change the update intervals and you'll be good to go!
Define in app delegate and access it elsewhere using the UIApplication sharedApplication.
Hope this helps
Related
I'm setting up a timer to fire at an interval of 13 mins. But the timer seems to be getting fired at irregular intervals. This is an intermittent issue. Some users reported the issue and I'm not able to reproduce the same.
- (void)resetIdleTimer
{
if (_idleTimer)
{
[_idleTimer invalidate];
[_idleTimer release];
_idleTimer = nil;
}
_idleTimer = [[NSTimer scheduledTimerWithTimeInterval:13*60.0
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO] retain];
}
- (void)idleTimerExceeded
{
// do some processing
[_idleTimer release];
_idleTimer = nil;
}
Timer will be reset (resetIdleTimer) depending on some conditions, but that's anyhow resets the timer with 13 mins.
When I looked into code, I can see only the issue that is not passing timer parameter to the selector. I'm not sure whether that's the reason for this issue or not ? Did anyone come across this kind of weird ness ? (Any how I will be updating the code to to have timer as an argument).
One user reported that its just happened after 4 mins itself.
Let confirm one thing,not passing timer parameter to the selector will not cause any issues.
Here is the updated code which works fine
#property(nonatomic,strong)NSTimer *myTimer;
or
#property(nonatomic,retain)NSTimer *myTimer;
Methods
- (void)resetIdleTimer
{
if ([self.myTimer isValid])
[self.myTimer invalidate];
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:13*60.0
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO];
}
- (void)idleTimerExceeded
{
NSLog(#"tiggered");
}
i don't find any error in your code, the only idea about is that a user enter your class creating an instance, let's call it instance "A", then start the timer, then exit your class before 13 minutes, then enter again with a new instance "B", a new timer starts...
at this point user expects your timer methods fires after 13 minutes... but timer of "A" is still pending (because there may be an error in the way you close your class) and executes...
You may try to add some logs and try to do as i said, and check all logs (in particular if you enter the dealloc, and that the log in idleTimerExceeded print instances equals to the last instances printed in resetIdleTimer):
-(void)resetIdleTimer {
if (_idleTimer)
{
[_idleTimer invalidate];
[_idleTimer release];
_idleTimer = nil;
}
NSLog(#"...resetIdleTimer: instance: %p", self);
_idleTimer = [[NSTimer scheduledTimerWithTimeInterval:13*60.0
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO] retain];
NSLog(#"...resetIdleTimer: _idleTimer: %p", _idleTimer);
}
- (void)idleTimerExceeded
{
// do some processing
NSLog(#"...idleTimerExceeded: instance: %p", self);
NSLog(#"...idleTimerExceeded: _idleTimer: %p", _idleTimer);
[_idleTimer release];
_idleTimer = nil;
}
-(void) dealloc
{
NSLog(#"...dealloc: instance: %p", self);
[super dealloc];
}
just an idea... but you may have forgotten that timers retain the instance passed in target:parameter (you pass "self", so your class instance is retained), so a common error is to forget to close the timer when closing the class instance.
a correct way should be:
-a root class (AAA) instantiate your class (BBB) with timer
-BBB start its timer
-AAA wanna dealloc/resetIdleTimer class BBB: before to release it must stop/invalidate BBB.timer (if not timer retain BBB, and BBB won't be released until timer fires/executes its method)
so, if it's your case... add a method that could be called from class AAA that stop/invalidate the timer, something like:
-(void)stopTimer{
if (_idleTimer)
{
[_idleTimer invalidate];
[_idleTimer release];
_idleTimer = nil;
}
}
If you want your timer to fire in exact 13 minutes, maybe this will help:
https://stackoverflow.com/a/3519913/1226304
I would like to call from a method 'method1' another method 'method2'. The problem is that there is a CADisplayLink on 'method1' and when I want to call 'method2' from 'method1' it call it at 6Ofps so 60 times per second, but I just want that it call it one time. I know that I have to use BOOL variable but I don't know how to use them . Can anyone help me ? sorry for my english I'm french :/
//EDIT:
there is a CADisplayLink on method1:
-(void)method1{
if(
if ( leScore % 20000 == 0) {
[self method2];
}
-(void)method2{
etatJeu = arc4random() / (UINT_MAX/3);
switch(etatJeu) {
case 0: /* top */
etatJeu=kEtatJeu2;
break;
case 1: /* bottom */
etatJeu=kEtatJeu3;
break;
case 2: /* bottom */
etatJeu=kEtatJeu4;
break;
default:
break;
}
so every time 'leScore % 20000 == 0' call one time method2.
If you want to make the method call happen only once, then use a bool this way:
#interface SomeClass {
BOOL method2RunFlag; // set to NO in init
}
#end
// ... in your method1
if( method2RunFlag == NO ) {
// call your method2
method2RunFlag = YES;
}
Based on your edited code above:
-(void)method1{
if( method2RunFlag == NO ) {
method2RunFlag = YES;
if ( leScore % 20000 == 0) {
[self method2];
}
// wait 1 second before able to call again
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(resetMethod2RunFlag:) userInfo:nil repeats:NO];
}
- (void)resetMethod2RunFlag:(NSTimer *)timer {
method2RunFlag = NO;
}
Still not entirely sure what you're after but this is my best guess. =)
You probably want to create 2 variations of method1, one to use with CADisplayLink, the other elsewhere, perhaps calling all the common code in a helper method1A, but with a flag parameter saying whether to call method2 or not.
first of all thanks for getting into my questions.
now, i tried a lot of different ways to call some function in my code but the ways i find it works (performselector:withobject:afterdelay) are get into the function immediately and i need the function to make some "if's" after random times.
Well you can see the code in here-
-(void)playGame{
isButtonPressed=NO;
int minimum=1;
int maximum=4;
int randomTime=(arc4random()%(maximum-minimum))+minimum;
[self performSelector:#selector(ifButtonNotPressed) withObject:nil afterDelay:randomTime];
}
-(void)closingAction{
NSLog(#"Closing Panel automatic");
if (randomFrog==1){
[self performSelector:#selector(animatePanelCloseAction::) withObject:gameImage1_up withObject:gameImage1_down];
}
[self performSelector:#selector(playGame) withObject:nil afterDelay:0.25];
}
-(IBAction)buttonAction:(id)sender{
if (sender == gameButton1 && randomFrog==1){
[self performSelector:#selector(disableAllButtons)];
score=score+1;
isButtonPressed=YES;
[scoreLabel setText:[NSString stringWithFormat:#"Score:%d",score]];
[self performSelector:#selector(closingAction)];
}
}
-(void)ifButtonNotPressed{
if (isButtonPressed==NO){
[self performSelector:#selector(closingAction)];
}
}
as you can see, im trying to check if button was pressed, but its going inside and check it immeditlly and perform it after delay.
how can i call it after real delay ?!
thank you all.
amir.
Try NSTimer to fix this issue.
if(callTimer)
{
[callTimer invalidate];
callTimer=nil;
}
callTimer=[NSTimer scheduledTimerWithTimeInterval:0.50 target:self selector:#selector(callFunction) userInfo:nil repeats:NO];
with help of this you can call any function at particular time interval. if function call again then it invalidates current timer and create new timer for that.
Dont forgot to invalidate timer after calling function means invalidate timer in particular function.
Hey you can use NSTimer class for the same.
- (void)FireTimer : (NSInteger) afterTimeInterval
{
if (afterTimeInterval > 0) //afterTimeInterval is in seconds
{
[NSTimer scheduledTimerWithTimeInterval: afterTimeInterval
target: self
selector: #selector(ResetListAfterExpiration:)
userInfo: nil
repeats: NO];
}
}
- (void)ResetListAfterExpiration: (NSTimer *)timer
{
// your actions
… … …
[timer invalidate]; // must be done to prevent memory leak
}
//Now You call the FireTimer method from where ever you want and it will wait for afterTimeInterval seconds and call the method ResetListAfterExpiration. That's your answer.
I have been looking for this answer from a while but I could not get find solution. Can any one please tell me how do we calculate time in back ground since user made any interaction with the app. In few websites if you don't interact with the web page for a while you will be logged out. I am looking for this functionality.
I would really appreciate your time.
Thanks
I think you can catch events via a UIApplication custom subclass. You can restart your idle timer there without mucking up all of your code. Here's how to do it:
Make your own UIApplication subclass:
#interface MyUIApplication : UIApplication {
NSTimer *idleTimer;
}
#property(nonatomic,retain) NSTimer *idleTimer;
In your main()
int retVal = UIApplicationMain(argc, argv, #"MyUIApplication", nil);
In your application subclass.m, override sendEvent: and sendAction:. See Apple docs here:
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
[self. idleTimer invalidate];
self. idleTimer = [NSTimer scheduledTimerWithTimeInterval:kIDLE_TIME target:self selector:#selector(logout:) userInfo:nil repeats:NO];}
... same for send action. You can put your logout logic in this class, too:
- (void)logout:(NSTimer *)timer {
// do logout
}
You should still logout (and invalidate the timer) in your app delegate when the app resigns:
- (void)applicationWillResignActive:(UIApplication *)application {
[application logout:nil];
}
You save a UNIX timestamp of the last interaction and then you compare it to a current one. If the difference between them is greater than your time limit, you log user out.
I would log the time when - (void)applicationWillEnterForeground:(UIApplication *)application, and when - (void)applicationWillEnterBackground:(UIApplication *)application, calculate the different, check if it exceed certain value, log off user from previous session.
// Extend method of UIApplication
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
// Only want to reset the timer on a Began touch or an Ended touch, to reduce the number of timer resets.
NSSet *allTouches = [event allTouches];
if ([allTouches count] > 0) {
// allTouches count only ever seems to be 1, so anyObject works here.
UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
if (phase == UITouchPhaseBegan || phase == UITouchPhaseEnded) {
[self resetIdleTimer:NO];
}
}
}
- (void) resetIdleTimer:(BOOL)force {
// Don't bother resetting timer unless it's been at least 5 seconds since the last reset.
// But we need to force a reset if the maxIdleTime value has been changed.
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
if (force || (now - lastTimerReset) > 5.0) {
// DebugLog(#"Reset idle timeout with value %f.", maxIdleTime);
lastTimerReset = now;
// Assume any time value less than one second is zero which means disable the timer.
// Handle values > one second.
if (maxIdleTime > 1.0) {
// If no timer yet, create one
if (idleTimer == nil) {
// Create a new timer and retain it.
idleTimer = [[NSTimer scheduledTimerWithTimeInterval:maxIdleTime target:self selector:#selector(idleTimeExceeded) userInfo:nil repeats:NO] retain];
}
// Otherwise reset the existing timer's "fire date".
else {
[idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:maxIdleTime]];
}
}
// If maxIdleTime is zero (or < 1 second), disable any active timer.
else {
if (idleTimer) {
[idleTimer invalidate];
[idleTimer release];
idleTimer = nil;
}
}
}
}
- (void) idleTimeExceeded {
NSLog(#"Idle time limit of %f seconds exceeded -- ending application", maxIdleTime);
// Force application to end
// self.dealloc; -- There's a dealloc in StartProcessViewController.m, but I'm not sure we ought to dealloc the UIApplication object.
_Exit(0);
}
One way to archive this is setting a cookie with a login hash that has an expire time of X minutes every time the user request a page. If the cookie isn't present the user is logged out. This could also be a session cookie. Of course that only works if you're talking about a webapp :)
I'm struggling with this. I saw some code where you can do this :
- (void)startTimer {
pauseTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(doActions) userInfo:nil repeats:YES];
}
Then call it in doActions.
Problem is i want to call it while a button is being pressed, and do actions is an IBaction. I keep getting a sigAbrt.
Can someone give me some sample code where you cay change a label from 'on' to 'off' every 1 second while a button is being pressed?
EDIT
i mean if doActions looks like this
- (IBAction) doActions {
for(int j; j<100; j++){
theLabel.hidden != theLabel.hidden;
//Does invalidate timer go here?
}
}
It still isn't clear to me, what you're trying to accomplish: I would have found it much easier, if you simply had it written down in plain english, after all.
That said, here's what I think will get you where you want to go:
// Assuming ivars:
// NSTimer* toggleTimer
// NSUInteger toggleCount
- (void)toggleTimerFired:(NSTimer*)timer
{
if ( toggleCount++ >= 100 ) {
[self stopToggling:self];
return;
}
// do whatever you need to do a hundred times here
}
- (IBAction)stopToggling:(id)sender
{
[toggleTimer invalidate], toggleTimer = nil; // you don't want dangling pointers...
// perform any other needed house-keeping here
}
- (IBAction)startToggling:(id)sender
{
[self stopToggling:self]; // if `startToggling:` will NEVER be called when a timer exists, this line CAN be omitted.
toggleCount = 0;
toggleTimer = [NSTimer scheduledTimerWithTimeInterval:1. target:self selector:#selector(toggleTimerFired:) userInfo:nil repeats:YES];
}
Depending on what exactly you want to do, startToggling: needs to be sent on either touchUpInside or touchDown. In the second case, stopToggling: probably needs to be called on any touchUp... event of the button.
- (void)startTimer {
pauseTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(doActions) userInfo:nil repeats:YES];
}
- (void) doActions {
theLabel.hidden != theLabel.hidden;
}