【问题标题】: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 默默地“修复”了它并且应用程序将无法运行。

有人知道吗?

【问题讨论】:

    标签: ios cocoa-touch background nstimer runloop


    【解决方案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 并直接从设备运行),您会发现应用程序被挂起的速度更快(除非您执行此后台任务)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多