Ожидается ли запуск NSTimer, когда приложение установлено на задний план?

Я вообще не понимаю, но NSTimer в моем приложении определенно работает в фоновом режиме. У меня есть NSLog в методе, запускаемом таймером, и он регистрируется, пока он работает в фоновом режиме. Это на iPhone 4 с iOS 4.2.1. Я объявил поддержку фона местоположения в Info.plist.

Я читаю документы и многие обсуждения здесь и в других местах, и это не должно быть возможным. Это ошибка iOS? Или недокументированная особенность? Я не хочу использовать его и узнаю в ближайшем будущем, например, с выходом iOS 4.3, что Apple молча "исправила" его, и приложение не будет работать.

Кто-нибудь знает больше об этом?

Ответ 1

NSTimer будет срабатывать всякий раз, когда запускается основная runloop. Apple не делает promises, что я знаю о нечеловеческих таймерах или для предотвращения запуска основной runloop. Это ваша обязанность отключать ваши таймеры и выделять ресурсы при переходе на задний план. Apple не собирается делать это за вас. Тем не менее, они могут убить вас за запуск, если вы не должны или используете слишком много секунд.

В системе много дыр, которые позволят запускать приложение, если оно не разрешено. Это было бы очень дорого для ОС, чтобы предотвратить это. Но вы не можете полагаться на это.

Ответ 2

Вы можете иметь огонь по таймеру в режиме фонового исполнения. Есть несколько трюков:

  • Вам нужно выбрать фоновое выполнение с помощью 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.