Question

Here's a code snippet I'm trying to get to work, but its loop won't stop the way that I want it to:

 - (IBAction)methodName:(UIButton*)sender 
{
        [self loopMethod];
}

-(void) loopMethod
{

   for( int index = 0; index < loopLimit; index++ ) 
   {
        //code I want to execute
        [self performSelector:@selector(loopMethod) 
                   withObject:nil 
                   afterDelay:2.0];   
   }
}

The code just keeps looping even though I've made the for loop finite. What I want is for the code to execute, pause for two seconds, and then run the loop while the int value is less than the loopLimit I've set.

It's been hinted that this performSelector:withObject:afterDelay: method may not be the right thing to use here but I'm not sure why or what is better to use here.

Any illuminating suggestions?

Was it helpful?

Solution

What's happening here is that the loop is running as quickly as possible, and the performSelector:... calls are happening at that speed. Then, at 2.0001, 2.0004, 2.0010, ... seconds later, method gets called.

The other thing (now that you've edited to make clear that the performSelector:... is ending up calling the same method that it's in, is that the value of your loop's index variable isn't saved between calls. Every time loopMethod is run, the code in the loop starts from the beginning: index is set to zero and counts up. That means that every time the method is run, you end up with loopLimit new calls pending, 2 seconds from then. Each one of those calls in turn spawns a new set, and so on, ad infinitum.

Every run of the loop is in fact finite, but the loop keeps getting run. You need some way to signal that the loop needs to stop, and you can't do that entirely within the loop method. You could put the counter (your index variable) into an ivar; that would make its value persistent across calls to loopMethod, but I think you want to look into using an NSTimer that repeats:

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(method:) userInfo:nil repeats:YES];

If you stick this into an ivar, you can keep track of how many times it fires and stop it later. There's a number of posts already on SO about updating text fields in a loop, using a timer like this: https://stackoverflow.com/search?q=%5Bobjc%5D+update+text+field+timer

OTHER TIPS

The reason it doesn't run every 2 seconds is because you are running through the loop, and shooting off that selector after a 2 second delay. In other words, there is no delay in between the loop. If i had to guess, it probably waits 2 seconds, then fires loopLimit times, correct?

For your function to work the way you want it to, it would need to be recursive.

-(void) methodName
{
   if(index < loopLimit) {
    //code you want to execute
    [self performSelector:@selector(**methodName**) withObject:nil afterDelay:2.0];   
   }
   index++;
}

This is a pretty awkward way to do this. An NSTimer is typically what you would use here instead, then you can stop the timer when you are done.

In a function you start the timer like this:

[self setIndex:0];
[self setTimer:[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(method:) userInfo:nil repeats:YES]];

then this method gets called every time:

-(void)method:(NSTimer *)timer {
    if(index >= loopLimit) {
         [[self timer] invalidate];
    }

    //Code you want to execute
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top