首页 文章

后台的NSTimer行为(addTimer:,beginBackgroundTaskWithExpirationHandler :)

提问于
浏览
3

Xcode 6.3.1 ARC Enabled, for iOS 8.3

我需要帮助理解我在应用程序进入后台后在我的应用程序中维护单例共享计时器时遇到的奇怪行为 .

以前我不关心这个NSTimer,因为它使用后台位置服务在后台更新了用户位置 .

但是,我想解决用户在后台或位置更新时拒绝位置更新的情况 . 位置和计时器齐头并进,为我的应用程序 .

我在以下主题和网站帖子中完成了相当多的信息......

Apple's - Background Execution

http://www.devfright.com/ios-7-background-app-refresh-tutorial/

http://www.infragistics.com/community/blogs/stevez/archive/2013/01/24/ios-tips-and-tricks-working-in-the-background.aspx

How do I make my App run an NSTimer in the background?

How do I create a NSTimer on a background thread?

Scheduled NSTimer when app is in background?

http://www.raywenderlich.com/92428/background-modes-ios-swift-tutorial

iPhone - Backgrounding to poll for events

根据这些信息,我能够推导出(2)保持计时器运行约8小时的方法,而没有iOS在分配180秒后终止应用程序 . (3分钟)执行时间由Apple文档承诺 . 这是代码:

AppDelegate.h (unchanged for Method #1 or #2)

#import <UIKit/UIKit.h>

@interface MFAppDelegate : UIResponder <UIApplicationDelegate>
{
    UIBackgroundTaskIdentifier bgTask;
    NSInteger backgroundTimerCurrentValue;
}

@property (retain, nonatomic) NSTimer *someTimer;

@end

AppDelegate.m (Method #1)

<...

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

NSLog(@"applicationDidEnterBackground");

backgroundTimerCurrentValue = 0;

[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];

self.someTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:true];

[[NSRunLoop currentRunLoop] addTimer:self.someTimer forMode:NSRunLoopCommonModes];

}

- (void)timerMethod
{
    NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining];

    if (backgroundTimeRemaining == DBL_MAX)
    {
        NSLog(@"Background Time Remaining = Undetermined");
    }
    else
    {
        NSLog(@"Background Time Remaining = %0.2f sec.", backgroundTimeRemaining);
    }

    if (!someBackgroundTaskStopCondition)
    {
        [self endBackgroundTask];
    }
    else
    {
        nil;
    }
}

......>

AppDelegate.m (Method #2)

<...

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    NSLog(@"applicationDidEnterBackground");

    backgroundTimerCurrentValue = 0;

    bgTask = UIBackgroundTaskInvalid;

    self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
        [application endBackgroundTask:bgTask];
    }];

    self.someTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:true];

}

- (void)timerMethod
{
    NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining];

    if (backgroundTimeRemaining == DBL_MAX)
    {
        NSLog(@"Background Time Remaining = Undetermined");
    }
    else
    {
        NSLog(@"Background Time Remaining = %0.2f sec.", backgroundTimeRemaining);
    }

    if (backgroundTimerCurrentValue >= 500)
    {
        backgroundTimerCurrentValue = 0;
        [self.someTimer invalidate];
        [[UIApplication sharedApplication] endBackgroundTask:bgTask];
    }
    else
    {
        NSLog(@"backgroundTimerCurrentValue: %ld", backgroundTimerCurrentValue);
        backgroundTimerCurrentValue++;
    }
}

......>

我已经模拟了(2)方法过夜~8小时 . (28,800秒)通过调整(backgroundTimerCurrentValue> = 500)的if语句 . 但是为了重新创建这个问题的结果,我使用了500秒 . 这显然超过180秒 . 这些是两种方法的结果:

enter image description here

...在self.someTimer的500次迭代之后,即500秒 .

enter image description here

Now, this was great, but after doing testing with iPhone 5s disconnected from Xcode, etc. Something strange happens... 我创建了一个单一视图应用程序来检查标签中self.someTimer的值 .

最多180秒标签使用正确的计时器值更新 . 180秒后有时候,应用程序似乎仍然可以从打开的应用程序的幻灯片中选择,但是当我点击屏幕将应用程序置于前台时,应用程序会快速重新启动 .

这种行为是Apple承诺的,即180秒后 . 并没有有效的endBackgroundTask:方法 . 方法部分是我认为无效的类型UIBackgroundTaskIdentifier,如方法#2和nil方法#1 .

所以我的问题如下:

1)有什么不同于当iPhone从断开连接时插入运行Xcode的Mac时,系统是否允许某些条件发生,例如看似无休止的后台执行?

2)我总是可以使用位置服务来启动/停止它们,以便定时器值从上次位置更新后的一些累积时间内继续更新,但有没有人开发此代码实际上在断开连接的iPhone上运行?

3)如果在问题2中如此,那么这是合法的,并且在我为App Store提交我的应用程序时会被接受吗?

在此先感谢您的帮助 . 干杯!

1 回答

  • 3

    是的,通过调试器运行应用程序将使其保持活动状态,否则它将在3分钟后终止 . 所以,你显然不能在 生产环境 应用程序中依赖该功能 .

    不,试图让应用程序保持活跃超过这段时间是不合法的(除非您已经请求适当的后台操作,因为您的应用程序具有合法且迫切的背景操作需求,例如它是音乐播放器,VOIP,导航应用程序等) . Apple可能会拒绝任何不符合App Store Review Guidelines第2.16条的应用程序 .

    有关有效后台操作的更全面讨论,请参阅适用于iOS的应用程序编程指南的Background Execution章节 .

相关问题