I am trying to figure out how to initiate a method call ever hour while my app is running.
I have created the following method which is started once the ViewController is loaded but the problem is, is that it runs once but then never again..
This is what my code looks like.
#define kMaxAliveTimeSeconds 5.0
// currently only running the query every 5 seconds for testing
- (void)deviceTimer {
if (!isAliveTimer) {
isAliveTimer = [NSTimer scheduledTimerWithTimeInterval:kMaxAliveTimeSeconds
target:self
selector:#selector(deviceIsAlive)
userInfo:nil
repeats:NO];
}
else {
if (fabs([isAliveTimer.fireDate timeIntervalSinceNow]) < kMaxAliveTimeSeconds-0.5) {
[isAliveTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:kMaxAliveTimeSeconds]];
}
}
}
- (void)deviceIsAlive {
// do stuff here
}
the only thing is deviceIsAlive is called once but then never again.
Either set repeats:YES in the timer allocation statement (so it repeats automatically, otherwise nstimer invalidates itself after the first time) or in your deviceIsAlive function allocate a new timer.
Related
I am using an NSTimer to call a method which is titled Lose. I had a timer which when it ran out, it called Lose, but I lost everything due to a hard drive error. After trying to code it all again, I can't seem to get the method to be called.
Timer = [NSTimer timerWithTimeInterval:timeMax target:self selector:#selector(Lose) userInfo:nil repeats:NO];
Lose is declared in my .h file, like this:
-(void)Lose;
Also, my method looks like this:
-(void)Lose{
Text.hidden = NO;
scoreLabel.hidden = NO;
Target.hidden = YES;
Targetx.hidden = YES;
if (Score > highScoreNumber) {
highScoreAchieved.hidden = NO;
highScoreNumber = Score;
}
}
The variable timeMax is an int declared in my .h file, like last time.
whenever a target is tapped in my game, timeMax becomes .03 seconds shorter. I do it like this:
timeMax = 5 - (Score * 0.03);
I don't remember it looking different before the massive hardware failure, but why isnt it working?
You have to schedule the timer on a run loop or just use this line instead which schedules it for you:
Timer = [NSTimer scheduledTimerWithTimeInterval:timeMax
target:self
selector:#selector(Lose)
userInfo:nil
repeats:NO];
It's also a good idea to always back up your code...which reminds me.
I am working on an iOS app which provides/makes calls from app. We can make two calls one after another. First time we are making 1st call. Once call get established, the NSTimer should be fired and it would show the duration of the call.
For this I am doing following for timer
self.switchTimer1 = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(setTimerLabel1:)
userInfo:nil
repeats:YES];
[self.switchTimer1 fire];
after establishing this first call, user can make second call.
Once second call gets established, it would fire second timer.
if (hasSecondCall)
{
self.switchTimer2 = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(setTimerLabel2:)
userInfo:nil
repeats:YES];
timeSec=0;
timeMin=0;
[self.switchTimer2 fire];
}
It's working fine while making first time of both calls.
Suppose if I ended 2nd call and again I made call, that time 2nd timer before establishing the call the timer automatically calling and once call established, the timer incrementing value very fast like double values showing. Like, 2,4, 6, etc.
For secondtimer after firing method following
- (void)setTimerLabel2:(NSTimer *)timer {
timeSec=timeSec+1;
NSLog(#"timeSec+1 %d",timeSec+1);
if (timeSec == 60)
{
timeSec = 0;
timeMin=timeMin+1;
}
NSString* timeNow = [NSString stringWithFormat:#"%02d:%02d", timeMin, timeSec];
[_switchCallLCD setStatus:timeNow labelNumber:2];
}
While disconnecting call we calling following
- (void)endingCallWithId:(UInt32)call_id {
if (hasSecondCall&& call_id==_current_call) {
if (self.switchTimer2) {
//NSLog(#"self.predictNumber %#",self.predictNumber);
self.predictNumber=self.predictNumber2;
[_lcd setText:self.predictNumber];
[self.switchTimer2 invalidate];
self.switchTimer2 = nil;
[self.switchTimer2 release];
[_switchCallLCD setStatus:NSLocalizedString(#"call ended",nil) labelNumber:2];
timeSec = 0;
timeMin = 0;
}
I have searched so many forums regarding this issue, but I couldn't figure out solution. I heard, if we use multiple timers there is some issue like this.
You likely still have the previous instance of the timer(s) running. When you make a second set of calls you then have 2 instances of switchTimer1 running and 2 instances of switchTimer2 running.
As soon as you end a call, you need to invalidate the timer:
[self.switchTimer1 invalidate];
and then set it to nil for good measure:
self.switchTimer1 = nil;
I cannot seem to work this one out. Here is my set up:
I have a function called requestDataWithCompletion:(someBlock)block. I call it when the class is initialised. The function requests certain motion data. I want to do this periodically, therefore, the first time I call this function, I specify some completion code which sets up a timer that re-calls this function periodically. The timer calls it via another function requestDataWithoutCompletion which simply calls the requestDataWithCompletion but with an empty block (so I don't keep creating timers);
- (void) requestDataWithCompletion:(someBlock)block {
// BREAK POINT 1
[self.motionManager queryActivityStartingFromDate:start toDate:[NSDate date] toQueue:self.queue withHandler:^(NSArray *activities, NSError *error) {
// BREAK POINT 2
// do some processing;
block();
}];
}
The block simply creates a timer on the main queue, which periodically recalls this function, but with no completion (since I don't want to keep creating more timers).
block = ^{
dispatch_async(dispatch_get_main_queue(), ^{
self.timer = [NSTimer scheduledTimerWithTimeInterval:timerInterval
target:self selector:#selector(requestDataWithoutCompletion) userInfo:nil repeats:YES];
});
}
- (void) requestDataWithoutCompletion {
[self requestDataWithCompletion^{;}];
}
The amazing thing is that despite this set up, my app is creating timer after timer! I can't understand why.
I have placed break points in requestDataWithCompletion method. One is outside the block submitted to NSOperationQueue to get activity data (BREAKPOINT 1) and one is inside the block submitted to NSOperationQueue. (BREAKPOINT 2). Basically it shows that each time the method is called by the timer, BREAKPOINT 1 has an empty completion block (as it should be) but then strangely BREAKPOINT 2 has the completion block I submitted when I first called the function when initialising the class. Therefore, it continues to create a new timer each time the function is called by the timer. Over time, this means a massive number of timers and then the app crashes!
I have a feeling this is something to do with NSOperationQueue, but I really don't know what it could be.
In your initialisation (or when you first want to get the data and then continue getting it):
self.timer = [NSTimer scheduledTimerWithTimeInterval:timerInterval target:self selector:#selector(requestDataWithoutCompletion) userInfo:nil repeats:YES];
[self.timer fire]; //get the activity data immediately.
- (void) requestDataWithoutCompletion {
[self requestDataWithCompletion:^{}];
}
With your original requestDataWithCompletion: method. (though you could get rid of requestDataWithCompletion: and put it's code directly in requestDataWithoutCompletion if you're not using it elsewhere)
I have the following pair of functions in a MessagePlayerViewController(UIViewController) which move a slider to reflect playback progress of an AVAudioPlayer:
-(void)startTrackingPlayback
{
if(!self.isPlaying)
{
self.isPlaying = YES;
self.playbackTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(updateProgress) userInfo:nil repeats:YES];
}
}
-(void)stopTrackingPlayback
{
if(self.playbackTimer)
{
if(self.playbackTimer.isValid)
{
[self.playbackTimer invalidate];
self.playbackTimer = nil;
}
}
self.isPlaying = NO;
}
Intermittently, and following no discernible pattern, I get an Exec Bad Access ith the top two items in the stack as:
0 objc_msgSend
1 [MessagePlayerViewController stopTrackingPlayback];
How can this be? I check if the timer exists before I call isValid and I check isValid before I invalidate it.
Using a breakpoint I can see that the timer does exist, but the error occurs when I set it to nil. If I remove this line, I get an identical error on the line:
[self.playbackTimer invalidate];
I would suggest inspecting the way you use your MessagePlayerViewController. It seems to me that both the stack trace and the behaviour you describe hint at the fact that it is the controller that is being deallocated earlier than your timer.
Take into account the fact that the run loop where the timer is scheduled will keep the timer alive.
Maybe the fix is as simple as calling invalidate in your controller's dealloc method (or somewhere else where it makes sense), but if you do not provide more code, it is not possible to say.
I'm having trouble with incrementing an int from one function by calling it in another function.
At the moment the bit I'm working on looks like this:
in the .h file I declare the int and timer as this:
int count;
NSTimer *sequenceOn;
in the .m file the segment of my function looks like this:
-(void) sequence {
count = 1;
while (count < (target)) {
sequenceOn = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(imagePlayer) userInfo:nil repeats:NO];
}
}
-(void)imagePlayer {
--CODE HERE FOR PLAYING ANIMATION--
count = count + 1;
}
All my other code is working fine and it should play through a series of images using the count value to decide which one to play. At the moment though it only plays the first animation and wont increment to the next one.
Any help would be much appreciated.
Thanks.
The timers will be fired from the run loop, which your code is blocking while running the loop. You probably want to have a timer regularly call your method:
-(void)stopTimer {
[sequenceOn invalidate];
sequenceOn = nil;
}
-(void)sequence {
[self stopTimer];
count = 1;
// start a repeating timer:
sequenceOn = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self
selector:#selector(imagePlayer) userInfo:nil repeats:YES];
}
-(void)imagePlayer {
//--CODE HERE FOR PLAYING ANIMATION--
count = count + 1;
if (count >= target) {
[self stopTimer];
}
}
I don't see anything in your while loop that increments count or decrements target, so either it's an infinite loop or it never executes, depending on the value of target.
You have the last argument of scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: set to NO, is that what you want, it will only fire once. If not you will have to give us more code to show how imagePlayer is being called repeatedly.