Как -performSelector: withObject: afterDelay: работать?

В настоящее время я работаю в предположении, что -performSelector:withObject:afterDelay: не использует потоки, но расписывает событие, которое будет запущено позднее в текущем потоке. Правильно ли это?

Подробнее, в частности:

- (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;
}

Гарантируется ли, что -resetIsBad не будет вызываться до тех пор, пока не вернется -methodCalledByButtonClick, если мы будем работать в основном потоке, даже если -methodCalledByButtonClick занимает сколь угодно длительное время для завершения?

Ответ 1

Из docs:

Вызывает метод приемника текущий поток, используя значение по умолчанию после задержки.

Далее обсуждается:

Этот метод устанавливает таймер для выполнения сообщение aSelector на текущем цикл выполнения потоков. Таймер настроен для работы в режиме по умолчанию (NSDefaultRunLoopMode). Когда таймер огни, поток пытается удалить из очереди сообщение из цикла запуска и выполните селектор. Успешно, если выполняется цикл выполнения и в режим по умолчанию; в противном случае таймер ждет, пока цикл выполнения не будет по умолчанию.

Из этого мы можем ответить на ваш второй вопрос. Да, он гарантировал, даже с более короткой задержкой, так как текущий поток занят, когда вызывается performSelector. Когда поток вернется в цикл выполнения и удалит селектор, вы вернетесь с вашего methodCalledByButtonClick.

Ответ 2

performSelector:withObject:afterDelay: планирует таймер в том же потоке, чтобы вызвать селектор после пройденной задержки. Если вы зарегистрируетесь в режиме запуска по умолчанию (т.е. Не используете performSelector:withObject:afterDelay:inModes:), я уверен, что он будет ждать, пока следующий проход пройдет через цикл выполнения, поэтому все в стеке будет завершено первым.

Даже если вы вызываете с задержкой 0, он будет ждать следующего цикла и будет вести себя так, как вам нужно. Для получения дополнительной информации см. документы.