17

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

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

有人知道更多吗?

4

2 回答 2

33

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

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

于 2011-03-03T23:16:52.963 回答
8

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

  • 您需要选择使用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 应用程序锁定后,计时器不会触发。
于 2013-07-03T10:32:07.243 回答