iOS:在后台运行一段时间后终止应用程序

3
我正在尝试在我的应用程序中实现一个密码锁定功能,让用户选择在重新输入密码之前必须经过多长时间(类似于操作系统的密码功能)。例如,用户可以选择在退出应用程序进入后台后的5、10或20分钟后需要输入密码。
我已经尝试以不同的方式呈现密码视图,但往往很难找到最好的呈现方式,因此我想也许在时间到期后终止应用程序是最好的方法,这样当启动应用程序时,我只需呈现密码屏幕。
这个可行吗?我有两种思路来解决这个问题。
1)在应用程序委托中使用NSTimer,在应用程序进入后台时启动它,然后当计时器到达设置的分钟数时,终止应用程序?我可以看到有很多事情会出错,例如如果操作系统比计时器早终止应用程序以释放内存,那么这不会是一个巨大的问题。
2)在应用程序进入后台时设置一个NSDate实例。然后当应用程序被启动时,查看这个日期是否超过x分钟,根据这个显示密码输入屏幕。
我感觉这两种方法都有点偏离了。我对计时器、RunLoops等缺乏经验,所以希望得到任何建议。

1
你可以在计时器中使用exit()调用...虽然这不被苹果的指南所推荐,但它确实可行。 - Richard J. Ross III
请考虑您选择的方法如何与用户在应用程序后台更改当前日期和时间的设备进行交互。有许多应用程序可以通过这种方式绕过“安全性”。 - Todd Masco
3个回答

2

第二个选项似乎是一个我们已经成功使用过的好解决方案。


2
选项2. 使用ApplicationDelegate生命周期方法来驱动它。
- application:didFinishLaunchingWithOptions: - applicationDidBecomeActive: - applicationWillResignActive: - applicationDidEnterBackground: - applicationWillEnterForeground: - applicationWillTerminate: - applicationDidFinishLaunching:
在applicationWillResignActive方法中将当前时间戳保存在UserDefaults中,在applicationWillEnterForeground中检查当前时间与时间戳的差值,如果经过了密码保护间隔时间,请呈现您的密码保护界面。 (最好在处于活动状态时清除时间戳,以减少接收通话和短信等情况下误触发的可能性)
根据敏感程度,您可能需要在进入前台之前准备视图来模糊敏感数据,以便在解锁状态下不会返回敏感数据。

谢谢 @Kevin。这是我所做的事情的更轻量级和更好的选择。 - darkheartfelt

1

你可以同时使用两个选项以获得更好的结果。例如,当应用程序从didFinishLaunchingWithOptions:激活时,使用选项2;当应用程序从- (void)applicationDidBecomeActive:(UIApplication *)application或- (void)applicationWillEnterForeground:(UIApplication *)application启用时,使用选项1。 选项1-最简单的方法是在后台运行循环中安排一个NSTimer。我建议您在应用程序委托中实现以下代码,并从applicationWillResignActive:中调用setupTimer。

- (void)applicationWillResignActive:(UIApplication *)application
{
[self performSelectorInBackground:@selector(setupTimerThread) withObject:nil];
}
-(void)setupTimerThread;
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  NSTimer* timer = [NSTimer timerWithTimeInterval:10 * 60 target:self selector:@selector(triggerTimer:) userInfo:nil repeats:YES];
  NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
  [runLoop addTimer:timer forModes:NSRunLoopCommonModes];
  [runLoop run];
  [pool release];
}

-(void)triggerTimer:(NSTimer*)timer;
{
  // Do your stuff
}

in appDelegate .h

 UIBackgroundTaskIdentifier bgTask;

在 appDelegate .m 文件中
- (void)applicationDidEnterBackground:(UIApplication *)application
 {

UIApplication*    app = [UIApplication sharedApplication];

// Request permission to run in the background. Provide an
// expiration handler in case the task runs long.
NSAssert(bgTask == UIBackgroundTaskInvalid, nil);

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    // Synchronize the cleanup call on the main thread in case
    // the task actually finishes at around the same time.
    dispatch_async(dispatch_get_main_queue(), ^{
        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;

        }
   });
}];

// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Do the work associated with the task.

    // Synchronize the cleanup call on the main thread in case
    // the expiration handler is fired at the same time.
    dispatch_async(dispatch_get_main_queue(), ^{
        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;

        }
    });
});
NSLog(@"app entering background");
/*
 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.
 */

}
或者您可以通过以下方式在后台线程上运行NSTimer(我有意地泄漏了线程对象):

-(void)startTimerThread;
{
  NSThread* thread = [[NSThread alloc] initWithTarget:self selector:@selector(setupTimerThread) withObject:nil];
  [thread start];
}

尝试使用上面的代码。我们同时使用这两个选项,对我们来说效果很好。祝你好运。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接