web-dev-qa-db-fra.com

applicationWillTerminate quand est-il appelé et quand il ne l'est pas

Bonjour, j'ai lu plusieurs questions sur SO concernant l'applicationWillTerminate pour être appelé et ne pas être appelé.

Je voulais résumer ce que j'ai compris car plusieurs messages parlent différemment.

  1. Pour IOS (sans multitâche), il est toujours appelé lorsque vous appuyez sur le bouton principal.

  2. Pour IOS 4 et plus

    une. il est appelé pas lorsque vous appuyez sur le bouton d'accueil (lorsque l'application passe en arrière-plan)

    b. il est appelé lors de la fermeture de l'application depuis le quai multi-tâches et si l'application a un indicateur de fin soudaine dans info.plist désactivée, sinon elle n'est pas appelée. (J'ai défini l'option "L'application doit recevoir les événements de l'application morts" et, même après la fermeture de l'application depuis le dock multitâche, la fonction de terminaison n'est pas appelée)

Sur cette base, j'avais quelques questions.

Est-ce une bonne pratique de définir l’application qui doit avoir le drapeau des événements morts? (J'ai défini l'option "L'application doit recevoir les événements de l'application morts" et même après la fermeture de l'application depuis le dock multitâche, la fonction de terminaison ne s'est pas appelée)

ou

L'enregistrement pour "UIApplicationWillTerminateNotification" est-il une meilleure chose à faire que le paramètre info.plist?

Fondamentalement, je dois travailler uniquement lorsque l'application se termine et NON lorsqu'elle passe en arrière-plan.

ou

EDIT (1): Lorsque l'application est terminée, les éléments suivants sont envoyés à l'APP. Comment puis-je l'attraper?

Signal de programme reçu: «SIGKILL».

EDIT (2):

Remarque: il n'est pas appelé dans IOS 4 ou plus lors du retrait du dock multitâche. Vous pourriez penser que c'est. Mais dans mon cas ce n'est pas le cas.

Je demande si quelqu'un sait pourquoi? Y a-t-il autre chose qui me manque? 

Notez également que je règle l'option "L'application doit recevoir les événements de l'application morts" et, même dans ce cas, l'appel n'est pas effectué.

EDIT (3):

La réponse à la question suivante n'a également pas fonctionné . applicationWillTerminate n'est pas invoqué

Quelqu'un confronté au même problème que moi?

61
Anand

En bref, à moins que vous ayez UIApplicationExitsOnSuspend dans votre Info.plist défini sur OUI, dans iOS4 et les versions ultérieures, rien ne garantit que applicationWillTerminate: sera appelé.

Comme la documentation dit:

Pour les applications prenant en charge l'exécution en arrière-plan, cette méthode est généralement pas appelé lorsque l'utilisateur quitte l'application car le l'application se déplace simplement à l'arrière-plan dans ce cas. Cependant, ceci La méthode peut être appelée dans les cas où l'application s'exécute l’arrière-plan (non suspendu) et le système doit y mettre fin pour certaines raisons

(Souligné par moi.)

Si vous devez faire quelque chose avant la sortie de l'application, vous devez le faire dans applicationDidEnterBackground:. Il n'y a aucun moyen d'attraper SIGKILL.

49
Stephen Darlington

Je vois que -applicationWillTerminate: est appelé avec le test suivant. Dans un nouveau projet (j'ai utilisé le modèle 'Single View Application'), ajoutez ce qui suit à AppDelegate:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    NSLog(@"%s", __PRETTY_FUNCTION__);

    __block UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        if (identifier != UIBackgroundTaskInvalid) {
            [[UIApplication sharedApplication] endBackgroundTask:identifier];
            identifier = UIBackgroundTaskInvalid;
        }
    }];

    dispatch_async(dispatch_get_main_queue(), ^{
        for (int i=0; i < 20; i++) {
            NSLog(@"%d", i);
            sleep(1);
        }
        if (identifier != UIBackgroundTaskInvalid) {
            [[UIApplication sharedApplication] endBackgroundTask:identifier];
            identifier = UIBackgroundTaskInvalid;
        }
    });
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

Cet exemple va démarrer une tâche en arrière-plan lorsque l'application entre en arrière-plan. La tâche est juste un délai de 20 secondes (avec une journalisation une fois par seconde) qui maintient l'application en arrière-plan (remarquez la différence entre l'exécution en arrière-plan et suspendue) suffisamment longtemps pour pouvoir être supprimée du commutateur d'applications.

Donc, pour le tester, lancez l'application, appuyez sur le bouton d'accueil pour envoyer l'application en arrière-plan, puis, avant que le délai de 20 secondes ne soit écoulé, supprimez l'application du commutateur d'applications. À la fin des années 20, -applicationWillTerminate: est appelé. Vous pouvez regarder la console dans Xcode pour vérifier que c'est bien le cas.

J'ai essayé cela dans le simulateur iOS pour iOS 5.1 et 6.1 (les deux iPhone) et je l'ai vu arriver dans les deux cas. J'ai également testé sur iPhone 4S sous iOS 6.1.2 et constaté le même comportement.

28
Andrew Hershberger

Comme je le sais, votre demande va mourir dans 3 situations.

  1. Terminé par l'utilisateur final, vous pouvez faire quelque chose dans -[UIApplication applicationWillEnterBackground:], auquel cas, -[UIApplication applicationWillTerminate:] ne sera PAS appelé.

  2. Déposée par le système, telle que la mémoire insuffisante, vous pouvez faire quelque chose dans -[UIApplication applicationWillTerminate:], auquel cas nous ne savons PAS si applicationWillEnterBackground: a été appelé;

  3. En panne, rien ne peut être fait si ce n’est à l’aide d’une sorte d’outil de signalement des collisions. (Édité: attraper SIGKILL est impossible)

4
DawnSong

Source: http://www.cocos2d-iphone.org/forum/topic/7386

J'ai copié mon code de sauvegarde d'état d'applicationWillTerminate vers applicationDidEnterBackground et j'ai également ajouté un booléen multitaskingEnabled afin que je puisse uniquement appeler la sauvegarde d'état dans applicationDidEnterBackground. PARCE QUE, il y a une instance sur un périphérique multitâche où applicationWillTerminate est appelée: Si l'application est au premier plan et que vous mettez le périphérique hors tension. Dans ce cas, applicationDidEnterBackground et applicationWillTerminate sont appelés.

3
Jonathon Hibbard

Comme nous le savons, l’application n’a que 5 secondes lorsque -applicationWillTerminate est appelé. Donc, si quelqu'un veut mettre à jour le serveur à ce stade. Puis utiliser Appel synchrone.

[NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:&error];

Remarque: - -applicationWillTerminate n'appellera pas si l'application est supprimée de l'état suspendu. L'état suspendu signifie que l'application ne fonctionne pas dans backgroupd. Une des solutions consiste à utiliser une tâche en arrière-plan.

1
Gagan Joshi