Je ne sais pas comment et quand l'utiliser beginBackgroundTaskWithExpirationHandler
.
Apple montre dans leurs exemples de l'utiliser en applicationDidEnterBackground
délégué, pour avoir plus de temps pour effectuer une tâche importante, généralement une transaction réseau.
Lorsque vous regardez sur mon application, il semble que la plupart de mes informations réseau soient importantes, et quand l'une d'entre elles est lancée, j'aimerais la terminer si l'utilisateur appuie sur le bouton d'accueil.
Est-il donc accepté / bonne pratique d'encapsuler chaque transaction réseau (et je ne parle pas de télécharger un gros morceau de données, il s'agit principalement d'un fichier XML court) beginBackgroundTaskWithExpirationHandler
pour être du bon côté?
Réponses:
Si vous souhaitez que votre transaction réseau se poursuive en arrière-plan, vous devrez l'envelopper dans une tâche en arrière-plan. Il est également très important que vous appeliez
endBackgroundTask
lorsque vous avez terminé, sinon l'application sera supprimée une fois le temps imparti écoulé.Les miens ont tendance à ressembler à ceci:
J'ai une
UIBackgroundTaskIdentifier
propriété pour chaque tâche d'arrière-planCode équivalent en Swift
la source
La réponse acceptée est très utile et devrait convenir dans la plupart des cas, mais deux choses m'ont dérangé:
Comme un certain nombre de personnes l'ont noté, le stockage de l'identificateur de tâche en tant que propriété signifie qu'il peut être écrasé si la méthode est appelée plusieurs fois, ce qui conduit à une tâche qui ne sera jamais terminée correctement jusqu'à ce qu'elle soit forcée de se terminer par le système d'exploitation au moment de l'expiration. .
Ce modèle nécessite une propriété unique pour chaque appel,
beginBackgroundTaskWithExpirationHandler
ce qui semble fastidieux si vous avez une application plus grande avec de nombreuses méthodes réseau.Pour résoudre ces problèmes, j'ai écrit un singleton qui s'occupe de toute la plomberie et suit les tâches actives dans un dictionnaire. Aucune propriété nécessaire pour suivre les identificateurs de tâches. Semble bien fonctionner. L'utilisation est simplifiée pour:
Facultativement, si vous souhaitez fournir un bloc d'achèvement qui fait quelque chose au-delà de la fin de la tâche (qui est intégrée), vous pouvez appeler:
Code source pertinent disponible ci-dessous (trucs singleton exclus par souci de concision). Commentaires / commentaires bienvenus.
la source
typedef
CompletionBlock? Simplement ceci:typedef void (^CompletionBlock)();
Voici une classe Swift qui encapsule l'exécution d'une tâche en arrière-plan:
La façon la plus simple de l'utiliser:
Si vous devez attendre un rappel de délégué avant de terminer, utilisez quelque chose comme ceci:
la source
begin
méthode, mais il est facile de voir comment ajouter cette fonctionnalité.Comme indiqué ici et dans les réponses à d'autres questions SO, vous ne souhaitez PAS utiliser
beginBackgroundTask
uniquement lorsque votre application passera en arrière-plan; au contraire, vous devez utiliser une tâche d'arrière - plan pour toute opération de temps dont l' achèvement vous voulez vous assurer même si l'application ne va dans l'arrière - plan.Par conséquent, votre code est susceptible de se retrouver parsemé de répétitions du même code standard pour appeler
beginBackgroundTask
et de manièreendBackgroundTask
cohérente. Pour éviter cette répétition, il est certainement raisonnable de vouloir emballer le passe-partout dans une seule entité encapsulée.J'aime certaines des réponses existantes pour ce faire, mais je pense que le meilleur moyen est d'utiliser une sous-classe d'opération:
Vous pouvez mettre l'opération en file d'attente sur n'importe quelle file d'attente et manipuler cette file d'attente comme bon vous semble. Par exemple, vous êtes libre d'annuler prématurément toutes les opérations existantes sur la file d'attente.
Si vous avez plus d'une chose à faire, vous pouvez enchaîner plusieurs opérations de tâche d'arrière-plan. Les opérations prennent en charge les dépendances.
La file d'attente des opérations peut (et devrait) être une file d'attente d'arrière-plan; ainsi, il n'est pas nécessaire de s'inquiéter de l'exécution de code asynchrone à l'intérieur de votre tâche, car l'opération est le code asynchrone. (En effet, cela n'a aucun sens d'exécuter un autre niveau de code asynchrone dans une opération, car l'opération se terminerait avant que ce code ne puisse même démarrer. Si vous deviez faire cela, vous utiliseriez une autre opération.)
Voici une sous-classe d'opération possible:
Il devrait être évident de savoir comment l'utiliser, mais au cas où ce ne serait pas le cas, imaginez que nous ayons une OperationQueue globale:
Donc, pour un lot de code qui prend du temps, nous dirions:
Si votre lot de code chronophage peut être divisé en étapes, vous souhaiterez peut-être vous retirer plus tôt si votre tâche est annulée. Dans ce cas, revenez prématurément de la fermeture. Notez que votre référence à la tâche à partir de la fermeture doit être faible ou vous obtiendrez un cycle de rétention. Voici une illustration artificielle:
Si vous avez un nettoyage à faire au cas où la tâche d'arrière-plan elle-même serait annulée prématurément, j'ai fourni une option
cleanup
propriété de gestionnaire (non utilisée dans les exemples précédents). Certaines autres réponses ont été critiquées pour ne pas avoir inclus cela.la source
J'ai implémenté la solution de Joel. Voici le code complet:
fichier .h:
Fichier .m:
la source