0

I wish to create a loop that increments an integer every ten seconds, and does this one hundred times. But when I use this code:

- (IBAction)loopTest:(id)sender {

}

- (IBAction)beginLoop:(id)sender {
  for (i=0;i<100 ;i++ ) {
    testingLoops++;
    NSString *feed = [NSString stringWithFormat: @"%d", testingLoops];
    self.feedLabel.stringValue = feed;
    [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(loopTest:)           userInfo:nil repeats:NO];
}

}

the application just displays the integer as 100 straight away. I have it so that it runs the beginLoop when I press a button. What's going on?

jscs
  • 63,694
  • 13
  • 151
  • 195
  • One misunderstanding might be that `[NSTimer scheduledTimerWithTimeInterval:10 ...]` does not "wait" or "sleep" for 10 seconds. – Martin R Nov 29 '13 at 07:29

2 Answers2

0

Your statement:

[NSTimer scheduledTimerWithTimeInterval:10
                                 target:self
                               selector:@selector(loopTest:)
                               userInfo:nil
                                repeats:NO];

does not delay your loop - rather for every iteration of the loop it schedules a timer to call loopTest:, a method you've defined to do nothing.

To use a timer to delay a loop you need to schedule a method which performs the remainder of the loop. In other words a non-loop method which performs the equivalent of one iteration of your loop and then schedules a time to perform the remainder.

Following your approach, but switching to use an implicit timer provided by performSelector:withObject:afterDelay as it is more convenient here, this gives us:

- (IBAction)beginLoop:(id)sender
{
   // start "loop"
   // note we only pass the current index and not the limit or delay
   // as there is no performSelector version which directly supports
   // passing three values to the selector
   [self doLoopIndex:@0];
}

- (void) doLoopIndex:(NSNumber *)objIndex
{
   // extract int from NSNumber - we use the later as the argument type so we can use performSelector below
   int index = objIndex.intValue;

   // do the work of one iteration
   NSString *feed = [NSString stringWithFormat: @"%d", index];
   self.feedLabel.stringValue = feed;

   // increment "loop" counter and schedule next iteration if needed
   // note the use of @(index) to create an NSNumber as an object is required
   index++;
   if (index < 100)
      [self performSelector:@selector(doLoopIndex:) withObject:@(index) afterDelay:1];
}

This is not the only way to achieve your goal. Using blocks and "Grand Central Dispatch" (GCD) is another and in this case has the advantage that passing three values: current index, limit and delay; is easier.

For example, a general loop with delay might be written as:

- (void) doLoop:(int)index                      // starting index
          limit:(int)limit                      // limit
          delay:(NSTimeInterval)delayInSeconds  // delay each iteration
           body:(void (^)(int))body             // block for loop body
{
   // invoke the body block
   body(index);

   // increment index and schedule next "iteration" if needed
   index++;
   if (index < 100)
   {
      dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
      dispatch_after(popTime, dispatch_get_main_queue(), ^(void)
      {
         [self doLoop:index limit:limit delay:delayInSeconds body:body];
      });
   }
}

See the documentation for the details of the dispatch_X methods and types.

This might look more complicated, but that is because it is more general. Using the above your particular loop becomes just:

- (IBAction)beginLoop:(id)sender
{
   [self doLoop:0 limit:100 delay:1 body:^(int index)
    {
       NSString *feed = [NSString stringWithFormat: @"%d", index];
       self.feedLabel.stringValue = feed;
    }];
}

Which of the above approaches, or using an explicit timer as your original code did, is appropriate for your use case is your choice - there is no single "right" answer.

HTH.

CRD
  • 52,522
  • 5
  • 70
  • 86
-1

Try this

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, i * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ yourtextfield.text=[NSString stringWithFormat:@"%d", i]; });

Minkle Garg
  • 1,397
  • 3
  • 13
  • 27
  • 1
    `NSTimer` *can* be used to for this purpose (if done correctly). It would be more helpful to point out the error in OP's code, and make suggestions to fix it. – Martin R Nov 29 '13 at 07:33