33

I am currently working under the assumption that -performSelector:withObject:afterDelay: does not utilize threading, but schedules an event to fire at a later date on the current thread. Is this correct?

More, specifically:

- (void) methodCalledByButtonClick {
  for (id obj in array) {
    [self doSomethingWithObj:obj];
  }
}

static BOOL isBad = NO;
- (void) doSomethingWithObj:(id)obj {
  if (isBad) {
    return;
  }
  if ([obj isBad]) {
    isBad = YES;
    [self performSelector:@selector(resetIsBad) withObject:nil afterDelay:0.1];
    return;
  }
  //Do something with obj
}

- (void) resetIsBad {
  isBad = NO;
}

Is it guaranteed that -resetIsBad will not be called until after -methodCalledByButtonClick returns, assuming we are running on the main thread, even if -methodCalledByButtonClick takes an arbitrarily long time to complete?

Ed Marty
  • 39,590
  • 19
  • 103
  • 156

2 Answers2

46

From the docs:

Invokes a method of the receiver on the current thread using the default mode after a delay.

The discussion goes further:

This method sets up a timer to perform the aSelector message on the current thread’s run loop. The timer is configured to run in the default mode (NSDefaultRunLoopMode). When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode.

From this we can answer your second question. Yes, it's guaranteed, even with a shorter delay since the current thread is busy executing when performSelector is called. When the thread returns to the run loop and dequeues the selector, you'll have returned from your methodCalledByButtonClick.

Ben S
  • 68,394
  • 30
  • 171
  • 212
12

performSelector:withObject:afterDelay: schedules a timer on the same thread to call the selector after the passed delay. If you sign up for the default run mode (i.e. don't use performSelector:withObject:afterDelay:inModes:), I believe it is guaranteed to wait until the next pass through the run loop, so everything on the stack will complete first.

Even if you call with a delay of 0, it will wait until the next loop, and behave as you want here. For more info refer to the docs.

John
  • 15,990
  • 10
  • 70
  • 110
Asher Dunn
  • 2,384
  • 16
  • 12
  • 1
    Is it guaranteed that a delay of 0 will still not cause it to run immediately? The docs say "not necessarily" but that's different than "necessarily not", so I'm curious – Ed Marty Dec 17 '09 at 16:26
  • It's always worked that way in my experience, but good point. I don't know if it is guaranteed. – Asher Dunn Dec 17 '09 at 17:17
  • I had the same issue and this worked for me [self performSelector:@selector(showLoginScreen) withObject:nil afterDelay:1.0]; – Vishnu Gupta Oct 29 '12 at 13:50
  • FYI blocks do not wait until the next pass through the run loop. – malhal May 04 '14 at 20:21