最近我一直在使用dispatch_after而不是performSelector: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%的过冲是奇怪的。
我在我的代码中添加了一个“软糖因子”,可以调整额外的延迟,但我觉得这很令人惊讶:
#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.
}
);
我应该做更多的分析,看看延迟是否有固定的增加加上延迟因子或直接的百分比延迟,或者可能是错误的一些非线性比例,但是现在一个简单的乘数似乎做得很漂亮好。
答案 0 :(得分:22)
您可能听说过Timer Coalescing and App Nap - 这有助于降低功耗。
您在此观察的是延迟系统事件达到某个“余地值”的效果,以便能够一起执行所有及时,“定时器合并”。这将增加CPU在功耗降低模式下的持续时间。
对于调度lib,有一个标志可用于提高“余地值”的准确性,这也最终影响计时器的准确性(见下文)。我不认为让定时器不必要准确是个好主意 - 例如移动设备。
我的怀疑是,dispatch_after
将使用具有特定余地值集的调度计时器,这是实现定义的。
您可以使用dispatch_source_set_timer()
使用调度库实现非常准确的计时器,您还可以在其中指定“余地值”。
另请参阅: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.
*