I have a strange issue with my countDown timer. This timer is counts down from a set time (i.e. 60 seconds). This bit of code is placed in myViewDidLoad method. Everything works unless I go back and load the view again. Every time the view loads, there is an increment of 1 second in the countdown.
For example:
First Load: 60, 59, 58...
Second Load: 60, 58, 56...
Third Load: 60, 57, 54...
My code is below. Does anyone know why this is happening? Do I need to release something somewhere? Thank you!
countDown=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self
selector:#selector(TimeOver) userInfo:nil repeats:YES];
Each time your view loads, you create a new timer, but the old ones still exist. In your timer's action method, TimeOver, you are decrementing an index variable that keeps track of the seconds, and each timer runs that method every time it fires. So, if you have three timers, the index will decrease by three each time.
You need to either not create new a new timer whenever your view loads or, better, destroy the timer when your view disappears:
[countDown invalidate];
countdown = nil;
and recreate it when it reappears.
Also, be aware that your timer's action method has an incorrect signature. It should be a method which returns nothing and takes one argument, which is the timer itself, like so:
- (void)timeOver: (NSTimer *)tim;
Also, Cocoa methods should not start with capital letters.
Related
I am using simple code to start a repetitive timer which calls a method after every 30 seconds.
[NSTimer scheduledTimerWithTimeInterval:30 target:self selector:#selector(refresh) userInfo:nil repeats:YES];
It starts properly but after some time it calls the 'refresh' method after every 2 or 3 seconds randomly. I am not modifying the timer or anything anywhere else in the code . Any guess what could be possibly going wrong .....
Thank You!!
Is it possible that this code is being run more than once, thus setting up multiple instance of a timer with a 30 second timeout, all firing at different times?
If that isn't the case, is it possible that some of the processing you're doing is perhaps blocking the run loop and causing the timer events to queue up?
I would suggest checking those possibilities.
Sorry to bother, but I am in a bit of a pickle and I was wondering if anyone here could give me a hand.
I am currently designing a game in which enemies appear on the left side of the screen (out of boundaries) and move to the right. I have used a number of codes (this is using Sparrow Framework) and pretty much the number of enemies increases as you beat them. i.e. lvl 1-> 1 enemy, lvl 2-> 2 enemies, lvl3-> 3 enemies, etc...
I am having some trouble though with producing enemies. I have them appearing on 1 of 5 set paths (path numbers in NSMutableArray), selected by a random number generator, however they often appear on the same path, 1 on top of the other.
To produce the enemies, i am running a number of methods:
addEnemy -> produces the enemies (animations) which then travel from left to right.
onTouchEnemy -> if i touch the enemy, they die. activates drawEnemies
drawEnemies -> calls addEnemy a number of times equal to your lvl. coded as:
for(int i = 0; i < level; i++){
[self performSelector:#selector(addEnemy) withObject:nil afterDelay:3.0];
}
Is there a way to program so that there is a delay between activating the produce enemies method? I tried this afterDelay, but for some reason the program just ignores the 3 second delay and just produces enemies all in 1 go. This is rather irritating since i would like them to appear in a more orderly fashion.
I thank anyone willing to give me a hand with this. Sjkato.
performSelector:withObject:afterDelay: appears to ignore its delay because of the way the code executes. That for loop will iterate almost instantly, queuing up 3 calls to the addEnemy method. After 3 seconds the addEnemy methods execute, almost all at the same time.
To get a better result, you should look at NSTimer. You could set an interval of 3 seconds and tell it to repeat (you can invalidate the timer after the desired number of enemies has been produced).
Something like:
// creates and returns a new timer
// the timer is retained by the run loop for as long as it is valid
// invalidating the timer will cause the runloop to release it.
myTimer = [NSTimer scheduledTimerWithTimeInterval:3.0
target:self
selector:#selector(addEnemy)
userInfo:nil
repeats:YES];
This will cause the addEnemy method to be fired once every 3 seconds. You should keep a tally of how many enemies you have already made, and after the last one is made, stop the timer so that it doesn't fire again.
if (numberOfDesiredEnemies == numberOfEnemiesProduced)
{
[myTimer invalidate], timer = nil;
}
Did you mean to do this, with a extra "*i" at the end? Like this:
for(int i = 0; i < level; i++){
[self performSelector:#selector(addEnemy) withObject:nil afterDelay:3.0 * i];
}
Try to look through NSTimer class.
There are some methods that provide ability to perform selectors with predefined delay and loop conditions.
I have an audio player and I want to show the current time of the the playback. I'm using a custom play class.
The app downloads the mp3 to a file then plays from the file when 5% has been downloaded. I have a progress view update as the file plays and update a label on each call to the progress view. However, this is jerky... sometimes even going backward a digit or two.
I was considering using an NSTimer to smooth things out. I would be fired every second to a method and pass the percentage played figure to the method then update the label.
First, does this seem reasonable?
Second, how do I pass the percentage (a float) over to the target of the timer. Right now I am putting the percent played into a dictionary but this seems less than optimal.
This is what is called update the progress bar:
-(void)updateAudioProgress:(Percentage)percent {
audio = percent;
if (!seekChanging) slider.value = percent;
NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];
[myDictionary setValue:[NSNumber numberWithFloat:percent] forKey:#"myPercent"];
[NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(myTimerMethod:)
userInfo:myDictionary
repeats:YES];
[myDictionary release];
}
This is called first after 5 seconds but then updates each time the method is called.
As always, comments and pointers appreciated.
UserInfo is a void *, so you could just pass an int for percentage directly, or just a pointer to your NSNumber object; it doesn't have to be an NSDictionary. Apart from that, using a dictionary is a standard approach, and doesn't have excessive overhead.
Note that you are creating a new timer with a 5 second repeat every time this method is called, which may or may not be your intention. When you create repeating timers (or normal ones) you should make sure to invalidate them when not required.
Can I send argument with #selector in NSTimer? If I want to release NSTimer, are the following steps right in dealloc?
[timer invalidate];
[timer release];
[timer release] only needs to be called if you "own" the timer. From Apple's documentation:
Because the run loop maintains the timer, from the perspective of memory management there's typically no need to keep a reference to a timer once you’ve scheduled it. Since the timer is passed as an argument when you specify its method as a selector, you can invalidate a repeating timer when appropriate within that method. In many situations, however, you also want the option of invalidating the timer—perhaps even before it starts. In this case, you do need to keep a reference to the timer, so that you can send it an invalidate message whenever is appropriate. If you create an unscheduled timer (see “Unscheduled Timers”), then you must maintain a strong reference to the timer (in a reference-counted environment, you retain it) so that it is not deallocated before you use it.
What does this mean?
If you alloc and init a timer, you must also release it, like so:
NSTimer * timer = [[NSTimer alloc] initWith...];
NSRunLoop * runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[timer release];
...
...
[timer invalidate];
timer = nil;
Once the timer has been added to the run loop, there is no reason to keep a reference to it anymore, since the run loops owns it. In this case, as shown, you would release the timer as soon as you add it to the run loop, and then simply invalidate it when you are finished. The final line (setting timer to nil) is for safety. The call to invalidate will result in the timer being released (by the run loop), so it is unsafe to keep a reference that points to it. Setting the local reference to nil keeps things kosher.
If, however, you create a timer using one of the convenience methods like so:
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval ...];
You do not need to call [timer release] at all! The convenience method adds the timer to the run loop, which then owns it, so you do not need to perform any memory management on the returned timer object. You would simply invalidate the timer when you no longer want to use it:
[timer invalidate];
timer = nil;
Or, if the timer was not set to repeat, you would do absolutely nothing at all, since it would be released after its first invocation.
The two methods do different things. If you own a timer (you retained it, or alloced it, or copied it) then you should release it. If you scheduled it on a run loop, then you must invalidate it for the run loop to release it. If you did both things, then you must release and invalidate the timer (however usually having the run loop owning the timer is sufficient).
Always, release is the last thing you do. Once you release something there is no guarantee it is safe to dereference the object, which means it no longer safe to send it ANY message.
That is the correct way to deallocate a timer that might still be running (and you want to stop).
I'm using AVAudioPlayer to play music in my iPhone app.
In a class that I wrote I have an array that contains random ascending integers. (2, 4, 9, 17, 18, 20,...)
These integers represent times in the song at which a certain event should occur. So if you take the above array, after 2 seconds of the song playing, some method should be called. After 4 seconds, another method should be called. And so on.
I have tried using a repeating NSTimer:
NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerTick) userInfo:nil repeats:YES];
Everytime it fires, it checks whether the value of the Audioplayer and of the current arrayindex are the same:
- (void) timerTick {
if([[myArray objectAtIndex:currentIndex] intValue] == (int)(player.currentTime)) {
//here the event-method is called
currentIndex++;
}
}
This code actually works, but only for some time. After some time however, myTimer and the timer that controls the musicplayer are out of sync. So it misses an element of myArray and an infinite loop starts. I don't know exactly why they get out of sync, but I think it could be because the timer and the player aren't being started at exactly the same time or maybe because of short performance lags.
I think I have to approach this in a totally different way. Is key-value observing a way to do this? I could add my class as an observer to the player object, so that it gets notified when the player.currentTime value changes. But that would cause a LOT of notifications to be send and I think it would be really bad for performance.
Any help much appreciated!
Ok here is my solution: I found an open source app that does almost the same thing my app should do, which helped me a lot.
I'm going to stick with the code I already have, with a little modification it should be precise enough for my purposes. Here it is:
currentIndex = 0;
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(timerTick) userInfo:nil repeats:YES];
- (void) timerTick {
if(timerRunning){
if([[myArray objectAtIndex:currentIndex] intValue] <= (int)(player.currentTime*100)) {
//some event code
currentIndex++;
}
}
}
The important change is from == to <= in the if-condition. When it gets asynchronous and misses an element of myArray, the error is corrected the next hundredth of a second. That's good enough.
Thanks for your help!
It could be that the timers are reasonably in sync, but your code just takes to long to execute (i.e: longer then 1 second).
Couldn't you just use the timer of the musicplayer, and spawn a thread each time an event should occur? This way the timer stays uninterrupted, and your thread will do what it needs to do (lets say the heavy stuff).
If you reall y need two timers, I guess you could create a background threads that keeps those two timers in sync, but I think you're asking for trouble there..
Real world synchronization with music is very hard because users can notice mis-syncs of just a tenth of a second or less. You might find that AVAudioPlayer is to simple for what you need. You might have to control the rate the music plays using AudioQueueServices so that you can sync the music to the code instead of the other way around. You could see time to fire your code coming up and then start the methods before the music actually plays. Done skillfully this would sync the music most of the time.