Вы заметили, что dispatch_after работает на слишком медленном уровне на 10% на устройствах iOS?
В последнее время я использую dispatch_after вместо executeSelector: withObject: afterDelay, когда я хочу запускать некоторый код после задержки. Код чище, он имеет доступ к охватывающей области, я могу поместить код в строку вместо написания метода выброса и т.д. И т.д.
Мой код может выглядеть так:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
delay * NSEC_PER_SEC),
dispatch_get_main_queue(),
^{
//Delayed-execution code goes here.
}
);
Однако недавно я обнаружил, что время от времени из этого кода работает довольно последовательно примерно на 10% медленнее, чем требуется. Если я попрошу задержку в 10 секунд, мой блок будет выполнен примерно через 11 секунд. Это на устройстве iOS. Время, похоже, довольно близко соответствует симулятору.
Код, который я использую для тестирования, довольно прост:
NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
delay * NSEC_PER_SEC),
dispatch_get_main_queue(),
^{
NSTimeInterval actualDelay = [NSDate timeIntervalSinceReferenceDate] - startTime;
NSLog(@"Requested delay = %.3f. Atual delay = %.3f", delay, actualDelay);
//Delayed-execution code goes here.
}
);
Я тестировал устройства с iOS 4S на iPad Air, и дополнительная задержка довольно последовательна. Я еще не тестировал более старое устройство, такое как iPhone 4 или iPad 2, хотя я скоро это сделаю.
Я мог бы ожидать 20-50 мс "отскока" в задержке, но непротиворечивость 10% - 11% нечетна.
Я добавил в код код "fudge factor", который настраивается для дополнительной задержки, но я нахожу это удивительным:
#define delay_fudge 0.912557 //Value calculated based on averages from testing.
NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
delay * delay_fudge * NSEC_PER_SEC),
dispatch_get_main_queue(),
^{
NSTimeInterval actualDelay = [NSDate timeIntervalSinceReferenceDate] - startTime;
NSLog(@"Requested delay = %.3f. Actual delay = %.3f", delay, actualDelay);
//Delayed-execution code goes here.
}
);
Я должен, вероятно, сделать больше анализа и посмотреть, есть ли фиксированное увеличение задержки плюс коэффициент задержки или прямая процентная задержка или, возможно, некоторый нелинейный масштаб ошибки, но на данный момент простой множитель, кажется, делает довольно хорошо.
Ответы
Ответ 1
Возможно, вы слышали о Timer Coalescing и App Nap, что помогает снизить энергопотребление.
То, что вы наблюдаете здесь, - это эффект отсрочки системных событий до определенного "значения скорости", чтобы иметь возможность выполнять их все вместе в один момент времени "Таймер коалесцирования". Это увеличит продолжительность работы CPU в режиме пониженной мощности.
Для отправки lib существует флаг, который может быть использован для повышения точности "значения отклонения", что также в конечном итоге влияет на точность таймера (см. ниже). Я не думаю, что это хорошая идея сделать таймеры ненужными точными - например, для мобильных устройств.
Мое подозрение в том, что dispatch_after
будет использовать таймер отправки с определенным набором значений, который определяется реализацией.
Вы можете реализовать довольно точные таймеры с диспетчером lib, используя dispatch_source_set_timer()
, где вы также можете указать значение "leeway value".
См. также: dispatch/source.h
/*!
* @typedef dispatch_source_timer_flags_t
* Type of dispatch_source_timer flags
*
* @constant DISPATCH_TIMER_STRICT
* Specifies that the system should make a best effort to strictly observe the
* leeway value specified for the timer via dispatch_source_set_timer(), even
* if that value is smaller than the default leeway value that would be applied
* to the timer otherwise. A minimal amount of leeway will be applied to the
* timer even if this flag is specified.
*
* CAUTION: Use of this flag may override power-saving techniques employed by
* the system and cause higher power consumption, so it must be used with care
* and only when absolutely necessary.
*/
#define DISPATCH_TIMER_STRICT 0x1
...
* Any fire of the timer may be delayed by the system in order to improve power
* consumption and system performance. The upper limit to the allowable delay
* may be configured with the 'leeway' argument, the lower limit is under the
* control of the system.
*