Lately I've been using dispatch_after instead of performSelector:withObject:afterDelay when I want to trigger some code after a delay. The code is cleaner, it has access to the enclosing scope, I can put the code in-line instead of writing a throw-away method, etc, etc.
My code might look like this:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
delay * NSEC_PER_SEC),
dispatch_get_main_queue(),
^{
//Delayed-execution code goes here.
}
);
However, I recently discovered that the time-to-excution from this code seems to run pretty consistently about 10% slower than requested. If I ask for a delay of 10 seconds, my block gets executed about 11 seconds later. This is on an iOS device. The times seem to match quite closely on the simulator.
The code I'm using to test this is pretty simple:
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.
}
);
I've tested on devices from an iOS 4S to an iPad Air and the extra delay is pretty consistent. I haven't yet tested on an older device like an iPhone 4 or an iPad 2, although I will do that soon.
I might expect 20-50 ms of "slop" in the delay, but a consistent 10% - 11% overshoot is odd.
I've added a "fudge factor" to my code that adjusts for the extra delay, but I find it surprising:
#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.
}
);
I should probably do more analysis and see if there is a fixed increase in delay plus a delay factor or a straight percent delay, or perhaps some non-linear scale to the error, but for now a simple multiplier seems to do pretty well.