DispatchQueue.main.asyncAfter不准确

问题描述:

我试图在延迟后调用一个函数。在iOS10上,我可以使用Timer.scheduledTimer,它在给定的延迟后确实调用了我的闭包。但是,在iOS9上,我使用DispatchQueue.main.asyncAfter,它以六秒的延迟调用我的闭包。DispatchQueue.main.asyncAfter不准确

我正在测试的延迟是60秒。 Timer.scheduledTimer在60秒后调用闭包,66秒后调用DispatchQueue.main.asyncAfter。因此,如果我安排60秒的两个延迟,则第二个延迟将在132秒后使用DispatchQueue.main.asyncAfter调用。

func delay(delay:Double, closure:@escaping()->()) { 
    NSLog("Calling method with delay of: \(delay)") 

    if #available(iOS 10.0, *) { 
     Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { (timer) in 
      closure() 
     } 
    } else { 
     // TODO: Is not accurate, delay of +- 6 seconds 
     DispatchQueue.main.asyncAfter(deadline: .now() + delay) { 
      closure() 
     } 
    } 
} 

调用延时函数的代码:

func scheduleStudy(minutes: Int, secondsFromNow: Int) { 
    // Study notification 
    if #available(iOS 10.0, *) { 
     self.addiOS10StudyNotification(minutes: minutes, secondsFromNow: secondsFromNow) 
    }else{ 
     self.addiOS9StudyNotification(minutes: minutes, secondsFromNow: secondsFromNow) 
    } 

    // Study timer 
    delay(delay: Double(secondsFromNow)) { 
     self.onStudy(minutes: minutes) 
    } 

    NSLog("Scheduled study for \(minutes) minutes in \(secondsFromNow) seconds from now.") 
} 

规划的通知和方法的调用使用Timer.scheduledTimer

2016-12-06 13:34:06.024714 Mattie[1386:360881] Calling method with delay of: 0.0 
2016-12-06 13:34:06.025072 Mattie[1386:360881] Scheduled study for 1 minutes in 0 seconds from now. 
2016-12-06 13:34:06.036953 Mattie[1386:360881] Calling method with delay of: 60.0 
2016-12-06 13:34:06.037191 Mattie[1386:360881] Scheduled pause for 1 minutes in 60 seconds from now. 
2016-12-06 13:34:06.052520 Mattie[1386:360881] Calling method with delay of: 120.0 
2016-12-06 13:34:06.053162 Mattie[1386:360881] Scheduled study for 1 minutes in 120 seconds from now. 
2016-12-06 13:34:06.066838 Mattie[1386:360881] Calling method with delay of: 180.0 
2016-12-06 13:34:06.067027 Mattie[1386:360881] Scheduled finish in 180 seconds from now. 

暂停正所谓:

2016-12-06 13:35:06.038307 Mattie[1386:360881] ON PAUSE 
2016-12-06 13:35:06.065389 Mattie[1386:360881] Added pause timer for 1 minutes 

计划在13:34 :06,叫在13时35分06秒

规划的通知和方法的调用使用DispatchQueue.main.asyncAfter

2016-12-06 13:36:48.845838 Mattie[1390:361681] Calling method with delay of: 0.0 
2016-12-06 13:36:48.847389 Mattie[1390:361681] Scheduled study for 1 minutes in 0 seconds from now. 
2016-12-06 13:36:48.854336 Mattie[1390:361681] Calling method with delay of: 60.0 
2016-12-06 13:36:48.854543 Mattie[1390:361681] Scheduled pause for 1 minutes in 60 seconds from now. 
2016-12-06 13:36:48.861424 Mattie[1390:361681] Calling method with delay of: 120.0 
2016-12-06 13:36:48.861601 Mattie[1390:361681] Scheduled study for 1 minutes in 120 seconds from now. 
2016-12-06 13:36:48.868464 Mattie[1390:361681] Calling method with delay of: 180.0 
2016-12-06 13:36:48.868644 Mattie[1390:361681] Scheduled finish in 180 seconds from now. 

暂停正所谓:

2016-12-06 13:37:54.865400 Mattie[1390:361681] ON PAUSE 
2016-12-06 13:37:54.897354 Mattie[1390:361681] Added pause timer for 1 minutes 

计划在13:36: 48,在13:37:54呼叫

asyncAfter只能保证至少等待指定的时间。

如果你想在iOS9上准确的时间间隔,你可能应该仍然在使用计时器。

Timer.scheduledTimer(timeInterval: delay, 
       target: self, 
      selector: #selector(executeClosure), 
      userInfo: nil, 
       repeats: false) 

使用executeClosure作为执行上次保存的闭包的函数。

+0

谢谢,这是有道理的。我认为,代码执行会延迟主线程六秒钟。在每次学习和暂停会话之间,我都连接到一个VPN。我最初也使用Timer for iOS9,但选择器参数导致了一些问题,因为该函数需要一个参数。 XCode希望我在我的方法中添加@objc。我会看到如何解决这个问题,并坚持两个iOS版本的计时器。 –

+3

我也遇到过这个问题('asyncAfter'不准确)。在我的情况下,它总是迟到10%,然后预计。我很惊讶这是我在这个主题上发现的唯一讨论,所以我只是想知道,你怎么知道'asyncAfter'只能保证等待至少和指定的'@ tristan-burnside一样长? :)我无法找到关于此事的任何官方文档。 – tadija

+0

当我试图推广一些推迟事件的功能并希望使用gcd完成时,我也陷入了这个问题,但最终让我自己使用了定时器,因为它很有意义。希望没有人像我一样浪费时间(〜5小时)。谢谢Tristan –