【发布时间】:2015-06-23 22:56:04
【问题描述】:
已启用 Xcode 6.3.1 ARC,适用于 iOS 8.3
我需要帮助了解我在应用程序进入后台后尝试在应用程序中维护单例共享计时器时遇到的奇怪行为。
以前我并不关心这个 NSTimer,因为它是使用后台位置服务在后台更新用户位置的。
但是,我想解决用户在后台拒绝位置更新或同时拒绝位置更新的情况。对于我的应用,位置和计时器齐头并进。
我在以下主题和网站帖子中研究了很多信息...
Apple's - Background Execution
http://www.devfright.com/ios-7-background-app-refresh-tutorial/
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 (方法 #1 或 #2 不变)
#import <UIKit/UIKit.h>
@interface MFAppDelegate : UIResponder <UIApplicationDelegate>
{
UIBackgroundTaskIdentifier bgTask;
NSInteger backgroundTimerCurrentValue;
}
@property (retain, nonatomic) NSTimer *someTimer;
@end
AppDelegate.m (方法#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 (方法#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 秒。以下是这两种方法的结果:
...self.someTimer 500 次迭代后,即 500 秒。
现在,这很棒,但是在 iPhone 5s 与 Xcode 断开连接等情况下进行测试后,发生了一些奇怪的事情......我制作了一个单视图应用程序来检查 self.someTimer 的值在标签中。
最长 180 秒。标签使用正确的计时器值更新。 180 秒后。有时更快,应用程序似乎仍然可以从打开的应用程序的幻灯片中选择,但是当我点击屏幕以将应用程序置于前台时,应用程序会快速重新启动。
这种行为是 Apple 承诺的,即 180 秒后。并且没有有效的 endBackgroundTask:method。方法部分是我认为无效的 UIBackgroundTaskIdentifier 类型,如方法 #2 和方法 #1 中的 nil。
所以我的问题如下:
1) iPhone 插入运行 Xcode 的 Mac 与断开连接时有何不同,系统是否允许某些条件发生,例如这种看似无休止的后台执行?
2) 我总是对定位服务开始/停止它们感到棘手,以便计时器值从上次位置更新以来过期的一些时间累积中继续更新,但是有没有人开发此代码以实际在断开连接的 iPhone 上运行?
3) 如果在问题 2 中是这样,那么当我将我的应用提交到 App Store 时,这样的内容是否合法并且会被接受?
提前感谢您的帮助。干杯!
【问题讨论】:
标签: ios iphone xcode background nstimer