以编程方式强制重启iPhone应用程序?

51

当用户按下“注销”按钮时,我想要通过编程方式重新启动我的iPhone应用程序。

有没有人可以分享代码示例?我读到过通过修改main.m文件可以实现这一点,但是我找不到相关的代码。

任何帮助都将不胜感激。


可能是正确退出iPhone应用程序的方法?的重复问题。 - Brad Larson
12
你实际上不能强制重启它,你可以强制结束它,但很可能会被拒绝。相反,你应该设计你的应用程序,这样你就能通过编程方式清除所有数据状态并自己重置它。 - Jason Coco
只要崩溃是由用户发起的,就不应该被拒绝。例如,银行应用程序可能会向用户发出警报,提示某些内容不同步或自上次活动以来时间过长,并且将不得不再次登录,并提供“立即退出”按钮(作为唯一选项)。这不是问题,因为用户预计会发生这种情况。并不是说这是重置/注销的最佳或唯一方式,但据我上次检查时,这是可以接受的,至少在苹果公司看来。 - user5306470
7个回答

66

注意:

虽然此问题被��答为“不可能”,但我认为这是一个新的iOS开发人员提出的有效问题,并且有一些他们可以做的事情,这可能正是他们想要的。

有一种方法可以从用户的角度“重新启动”您的应用程序,而不是技术上重新启动或退出iOS应用程序。正如其他答案所指出的那样,iOS应用程序永远不应该明确退出,因为在iOS上不允许这样做。

我的答案:

如果您想让您的应用程序返回到启动时的状态,这并非完全可能,但我将解释一种方法来实现大部分功能,对于所有有效目的都应该足够。

首先要做的是重新创建根视图控制器。我建议从应用程序委托中的一个方法中执行此操作,就像这样:

- (void)resetAppToFirstController
{
    self.window.rootViewController = [[MyMainViewController alloc] initWithNibName:nil bundle:nil];
}

在许多情况下,这将足够,但是您还应该在此方法中重置任何应用程序状态。例如,注销用户,重置任何非持久状态并将所有可释放的对象设置为null。此方法也可用于从application:didFinishLaunchingWithOptions最初创建您的第一个视图控制器。

框架类和单例:

您将无法完全重置任何框架单例或每个应用程序实例的状态,例如以下内容:

[UIApplication sharedApplication];
[NSNotificationCenter defaultCenter];
[NSUserDefaults standardUserDefaults];
[UIScreen screens];
// etc...

这个做法应该是可以的,因为你不应该在这些对象中存储任何非持久状态(除了NSNotificationCenter,但是当对象被释放时,所有注册的观察者都应该被移除)。如果您确实想要初始化或重置任何框架状态,可以在同一个resetAppToFirstController方法中执行。无论如何,没有必要重新创建这些对象,或者window对象。

如果您有自己的单例对象,可以通过将它们存储在一个单例持有类中来重新创建它们(该类本身也是一个真正的单例对象)。从概念上讲,这是一个简单的单例类,其中每个其他单例对象都有一个属性和一个reset方法来清空并释放它们。您的其他单例对象应该使用这个类(而不是静态或全局变量)来存储单例实例。如果您使用任何第三方库,要小心,因为它们可能也使用单例对象,您需要确保它们也使用您的单例持有类,以便您可以根据需要重置它们。我认为这种技术是一个好的实践方式,因为在某些情况下(例如单元测试),您确实希望通常为单例的对象消失并重新初始化到原始状态。但是,您不希望将单例实现与单例持有者耦合在一起,因此实现这一点的好方法是使用NSMutableDictionary作为相关对象。将单例类名称作为键存储在[UIApplication sharedApplication]上。不过,我离题了,因为这是一个超出本问题范围的更高级的技巧。


以上内容应该足以让用户感觉应用已经“重置”。如果需要,您甚至可以再次显示启动画面,作为您的第一个视图控制器。


20

首先,虽然可以强制关闭您的应用程序,但这是不允许的,并将被苹果拒绝。即使没有被拒绝,一旦应用程序被杀死,就没有重新启动应用程序的方法。正如Jason Coco所说,您只需要通过代码找到一些重置应用程序的方法。这可能需要更多的工作,但值得避免被苹果拒绝。


相关的指南在这里:https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/MobileHIG.pdf - Julian
27
以上评论的意思是:不要通过编程方式突然退出iOS应用程序,因为人们往往会将其解释为应用崩溃。但是,如果外部情况阻止应用程序按预期功能运行,则需要告知用户该情况并说明他们可以采取什么措施。因此,在没有明确经过用户确认的情况下,不要突然退出应用程序。但如果是用户自己发起退出,而且有相应的说明,苹果公司是不会有任何问题的。我们在4个获批准的应用程序中使用exit(0)。 - Julian

15

试试这个,它对我有效。

-(void)restart
{
    MyAppDelegate *appDelegate = (MyAppDelegate *)([UIApplication sharedApplication].delegate);
    [appDelegate.navigationController popToRootViewControllerAnimated:NO];
    UIViewController *topViewController = appDelegate.navigationController.topViewController;
    Class class = [topViewController class];
    NSString *nibName = topViewController.nibName;
    UIViewController *rootViewcontroller = (UIViewController *)([[class alloc] initWithNibName:nibName bundle:nil]);
    [appDelegate.navigationController.view removeFromSuperview];
    appDelegate.navigationController.viewControllers = [NSArray arrayWithObject:rootViewcontroller];
    [appDelegate.window addSubview:appDelegate.navigationController.view];
    [appDelegate.window makeKeyAndVisible];
}

2
是的,我知道,这个想法是将应用程序设置为尽可能接近“初始”状态的状态... 这个技巧仅适用于基于导航控制器的应用程序,但这个想法很容易适用于其他情况。 - roberto.buratti

8

Objective-C:

exit(0);

Swift:

exit(0)

我有两个正在运行的应用程序,它们都没有被拒绝。我的其中一个应用程序甚至在主页上将这行代码作为一个功能放置在右上角,就像 Twitter 上的推文按钮一样。所以不用担心苹果会拒绝,除非它看起来像是意外崩溃。

enter image description here


4
这句话的意思是"它不是重新启动,而是退出"。 - user924
1
这会导致应用被App Store拒绝吗? - Edu

5

以下是如何在模拟器上使用私有API执行此操作:

    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", scheme, endpointString]];

    Class pClass = NSClassFromString(@"BKSSystemService");
    id service = [[pClass alloc] init];

    SEL pSelector = NSSelectorFromString(@"openURL:application:options:clientPort:withResult:");
    NSMethodSignature *signature = [service methodSignatureForSelector:pSelector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = service;
    NSString *app = @"com.apple.mobilesafari";
    [invocation setSelector:pSelector];
    [invocation setArgument:&URL atIndex:2];
    [invocation setArgument:&app atIndex:3];
    unsigned int i = [service performSelector:NSSelectorFromString(@"createClientPort")];
    [invocation setArgument:&i atIndex:5];
    [invocation invoke];
    exit(0);

如果具备适当的权限,这也适用于越狱应用程序。

对于其他应用程序,可以使用简单的 HTML 页面:

    NSString *format = @"https://dl.dropboxusercontent.com/s/rawt1ov4nbqh4yd/launchApp.html?scheme=%@&URL=%@";
    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:format, scheme, endpointString]];
    [[UIApplication sharedApplication] openURL:URL];
    exit(0);

很抱歉,这种情况需要互联网连接。

这是一个有趣的内部视图。当然,它使用了私有的苹果API,因此无法被放入AppStore中。我点赞了它,因为我只需要一个用于测试目的的解决方案。不幸的是,你的第一段代码在iOS 9上不再起作用(可能方法签名已更改…)-它适用于iOS 8.4模拟器。第二个代码可以在任何模拟器上运行,但是找出HTML代码应该做什么有点棘手… - konran

4
把这段代码放在一个UIAlertAction中,提示用户“保存并退出”。它会使exit(0)动画化,至少看起来是计划好的。
- (void)saveAndQuit
{
    [UIView animateWithDuration:0.8 animations:^{
        self.window.alpha = 0.0; // fade out...
        // ... while pinching to a point
        self.window.transform = CGAffineTransformScale(
                CGAffineTransformMakeTranslation( 0, 0 ), 0.1, 0.1 );
    } completion:^(BOOL finished) {
        exit(0);
    }];
}

3

如果应用程序在启动屏幕上且苹果已经接受了多年,则我会在-applicationWillResignActive:期间退出。对用户而言,它看起来不像崩溃。下次用户从图标启动应用程序时,它将不会出现任何异常。在某些情况下,针对已启动的应用程序进行额外检查以避免退出可能有助于提高用户体验。

- (void)applicationWillResignActive:(UIApplication *)application
{
    // called if phone-call comes in!
    if([gameController isGameFinished])
        exit(0);
}

1
这是程序退出,而不是重新启动。 - user924
由您来保存应用程序的状态并在下次启动时恢复它,这实际上是重新启动。在旧版的iOS中,由于切换应用程序涉及退出,因此必须无论如何保存状态。 - cat

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