当应用程序进入后台时,DispatchQueue.asyncAfter的行为如何?

时间:2018-07-26 13:53:53

标签: ios swift multithreading

我想知道DispatchQueue.asyncAfter(deadline:execute:)在背景情况下的表现。我试图在documentation中找到更多信息,但是那里什么也没有。

假设我在其中调用:
DispatchQueue.main.asyncAfter(deadline: .now() + 60.0) { ... }

  1. 如果我说10秒后回到后台会发生什么。
  2. 是否要在后台模式下完成?
  3. 如果没有,我们假设我将在2分钟后重新打开该应用程序。它会立即还是在剩下的50秒后调用关闭函数?

对我而言,最有趣的部分是知道倒计时60秒的计时器何时停止和恢复。

更新

寻找DispatchQueue.asyncAfter(deadline:execute:)DispatchQueue.asyncAfter(wallDeadline:execute:)之间的差异,我从Apple Staff(source)找到了该信息,该信息实际上回答了以下问题:

  

顺便问一下,您知道为什么Swift Dispatch库中提供了这两种Distict类型吗?

     

这些模型模拟墙时间(由gettimeofday返回)和马赫绝对时间(由mach_absolute_time返回)之间的差。后者与系统时钟变化无关,但是在您睡眠时停止。在C API中,所有内容都会展平到绝对马赫时间,这意味着您丢失了关键信息。考虑一下,如果您将计时器安排在将来的1秒钟,然后系统休眠2秒钟,将会发生什么情况。

为了清楚起见,DispatchQueue.asyncAfter(deadline:execute:)使用绝对时间,DispatchQueue.asyncAfter(wallDeadline:execute:)使用gettimeofday(墙上时间)。

这里还有另一个来源:What is the difference between dispatch_time and dispatch_walltime and in what situations is better to use one or the other?

测试

基于此,我准备了测试以确认这一点:

override func viewDidLoad() {
    super.viewDidLoad()

    self.textView.text.append(
        "expected times: \n1. \(Date().addingTimeInterval(60.0 * 2))\n" +
        "2. \(Date().addingTimeInterval(60.0 * 3))\n" +
        "3. \(Date().addingTimeInterval(60.0 * 5))\n\n")

    DispatchQueue.main.asyncAfter(deadline: .now() + 60.0 * 2) {
        self.textView.text.append("\n\(Date()) fired based on ABSOLUTE time (1)!")
    }

    DispatchQueue.main.asyncAfter(deadline: .now() + 60.0 * 3) {
        self.textView.text.append("\n\(Date()) fired based on ABSOLUTE time (2)")
    }

    DispatchQueue.main.asyncAfter(deadline: .now() + 60.0 * 5) {
        self.textView.text.append("\n\(Date()) fired based on ABSOLUTE time (3)!")
    }

    DispatchQueue.main.asyncAfter(wallDeadline: .now() + 60.0 * 2) {
        self.textView.text.append("\n\(Date()) fired based on WALL time (1)!")
    }

    DispatchQueue.main.asyncAfter(wallDeadline: .now() + 60.0 * 3) {
        self.textView.text.append("\n\(Date()) fired based on WALL time (2)")
    }

    DispatchQueue.main.asyncAfter(wallDeadline: .now() + 60.0 * 5) {
        self.textView.text.append("\n\(Date()) fired based on WALL time (3)!")
    }
}

结果

我在带有独立调试器的真实设备上运行它并锁定了手机。在恢复应用程序3.5分钟后,有:

  

根据WALL时间触发(1)!
  根据WALL时间(2)触发!

这两个事件正是在我还原应用程序时触发的。这证明了上面的说法。基于绝对时间的事件随后出现,这表明它们的计时器已停止。

3 个答案:

答案 0 :(得分:2)

简而言之,当应用暂停后(当用户转到另一个应用时),所有执行都会停止,除非您将其配置为后台执行,否则该计时器不会触发。参见App programming Guide for iOS: Background Execution

此外,请警惕从Xcode进行测试,因为附加到调试器可能会更改应用程序的生命周期。

答案 1 :(得分:2)

在前台启动的计时器将在后台运行,如果该应用已出于其他原因在后台运行(例如,它正在播放音乐,并且已打开音频的后台模式,或者它正在执行后台“核心定位”,并且已打开“核心定位”的背景模式)。

否则,它将在后台暂停。

答案 2 :(得分:0)

这取决于您的应用程序如何配置后台模式。例如,如果您启用了后台模式,并且设置位置监视器始终保持打开状态,那么即使用户未关闭应用程序,也可以永远在后台运行。因此,在这种情况下,将调用DispatchQueue.asyncAfter回调。

另一方面,如果禁用了后台模式,则应用程序在进入后台时将进入冻结状态,因此不会调用您的回调。在我的测试中,我注意到以下流程:调度队列不会停止,因此,如果您在60秒内调度调度,并且在后台停留100秒钟,那么当回到前台时,将立即调用回调。如果您仅在后台停留30秒钟,那么当您回来时,您将不得不再等待30秒钟。

请注意,如果使用后台任务,您仍然可以禁用后台模式,并保持应用程序在后台运行3分钟。

self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
     // called when the app is about to enter in freez state
     [[UIApplication sharedApplication] endBackgroundTask:weakSelf.backgroundTask];
}];