当应用程序后台运行时,NSTimer 是不是会触发?

Posted

技术标签:

【中文标题】当应用程序后台运行时,NSTimer 是不是会触发?【英文标题】:Is NSTimer expected to fire when app is backgrounded?当应用程序后台运行时,NSTimer 是否会触发? 【发布时间】:2011-07-08 10:44:47 【问题描述】:

我完全不明白,但我的应用程序中的NSTimer 肯定是在后台运行的。我在计时器运行的方法中有一个NSLog,它在后台运行时正在记录。它在带有 ios 4.2.1 的 iPhone 4 上。我已经在 Info.plist 中声明了位置背景支持。

我在这里和其他地方阅读了文档和许多讨论,这应该是不可能的。这是一个iOS错误吗?还是未记录的功能?我不想使用它并在不久的将来发现,例如随着 iOS 4.3 的到来,Apple 默默地“修复”了它并且应用程序将无法运行。

有人知道吗?

【问题讨论】:

【参考方案1】:

NSTimer 将在主运行循环运行时触发。据我所知,Apple 没有做出任何取消计划计时器或阻止主运行循环运行的承诺。当您移动到后台时,您有责任取消安排您的计时器并释放资源。苹果不会为你做这件事。但是,它们可能会因为你不应该跑步或使用太多秒而杀死你。

系统中有许多漏洞允许应用在未经授权的情况下运行。操作系统要防止这种情况会非常昂贵。但你不能依赖它。

【讨论】:

好的,这对我来说已经足够了。谢谢。我不知道我是否错过了什么。我肯定不会使用它。 我正在观察你所说的行为。我使用计时器每 10 分钟刷新一次令牌。如果应用程序在后台运行 15 分钟,然后在前台运行,则计时器将触发。 我没想到会这样。以为计时器不再在后台运行,什么也没有发生。似乎不知何故,所有未触发/未失效的计时器都由运行循环强烈存储,并且如果通过了fireDate,那么它们就会立即被触发。那么我可以安全地依靠 fireDate 获得通过还是我应该存储一个日期并与之进行比较?【参考方案2】:

您can 在后台执行模式下触发了计时器。有几个技巧:

您需要使用beginBackgroundTaskWithExpirationHandler 选择进入后台执行。 如果在后台线程上创建 NSTimer,则需要手动将其添加到 mainRunLoop。
- (void)viewDidLoad

    // Avoid a retain cycle
    __weak ViewController * weakSelf = self;

    // Declare the start of a background task
    // If you do not do this then the mainRunLoop will stop
    // firing when the application enters the background
    self.backgroundTaskIdentifier =
    [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^

        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundIdentifier];
    ];

    // Make sure you end the background task when you no longer need background execution:
    // [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];


    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^

        // Since we are not on the main run loop this will NOT work:
        [NSTimer scheduledTimerWithTimeInterval:0.5
                                         target:self
                                       selector:@selector(timerDidFire:)
                                       userInfo:nil
                                        repeats:YES];

        // This is because the |scheduledTimerWithTimeInterval| uses
        // [NSRunLoop currentRunLoop] which will return a new background run loop
        // which will not be currently running.
        // Instead do this:
        NSTimer * timer =
        [NSTimer timerWithTimeInterval:0.5
                                target:weakSelf
                              selector:@selector(timerDidFire:)
                              userInfo:nil
                               repeats:YES];

        [[NSRunLoop mainRunLoop] addTimer:timer
                                  forMode:NSDefaultRunLoopMode];
        // or use |NSRunLoopCommonModes| if you want the timer to fire while scrolling
    );


- (void) timerDidFire:(NSTimer *)timer

    // This method might be called when the application is in the background.
    // Ensure you do not do anything that will trigger the GPU (e.g. animations)
    // See: http://developer.apple.com/library/ios/DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW47
    NSLog(@"Timer did fire");

备注

应用只能在后台执行大约 10 分钟 - 在此之后计时器将停止触发。 从 iOS 7 开始,当设备锁定时,它几乎会立即暂停前台应用程序。锁定 iOS 7 应用后,计时器不会触发。

【讨论】:

代码非常平静,这将帮助我完成我的应用程序,THNX :)。一个小问题,“当应用程序在后台时可能会调用此方法”是什么意思?我想用它在 3 小时内从服务器更新一次数据。有更好的方法吗? 您的应用程序在进入后台后最多只能运行 10 分钟。从 iOS 7 开始,这 10 分钟可能是不相交的。您或许可以使用 iOS7 的新后台模式:后台传输用于大容量上传/下载或静默推送以远程唤醒您的应用。 此代码示例表明应用程序本身的前台/后台状态与主队列与全局队列问题之间存在混淆。无需将定时器的调度分派到全局队列。只需像往常一样在主队列上安排计时器,beginBackgroundTaskWithExpirationHandler 将使应用程序保持活动几分钟。 @Rob 我让它发射 180 秒,而无需将其包裹在 beginBackgroundTaskWithExpirationHandler...您建议使用 beginBackgroundTaskWithExpirationHandler 的任何原因? 当您运行附加到 Xcode 调试器的应用程序时,您可能正在这样做,这会改变应用程序的生命周期。在未连接到 Xcode 的情况下运行它(例如,退出 Xcode 并直接从设备运行),您会发现应用程序被挂起的速度更快(除非您执行此后台任务)。

以上是关于当应用程序后台运行时,NSTimer 是不是会触发?的主要内容,如果未能解决你的问题,请参考以下文章

当 runloop 被阻塞时,NSTimer 不会触发

ios nstimer 在后台运行以检查警报时间是不是等于当前时间并播放音频

使NSTimer在后台运行在物理设备上

在后台运行 NSTimer 方法

快速 NSTimer 在后台

如何在后台线程上创建 NSTimer?