Estoy un poco confundido acerca de cómo y cuándo usarlo beginBackgroundTaskWithExpirationHandler
.
Apple muestra en sus ejemplos usarlo en applicationDidEnterBackground
delegado, para tener más tiempo para completar alguna tarea importante, generalmente una transacción de red.
Al mirar mi aplicación, parece que la mayoría de las cosas de mi red son importantes, y cuando se inicia una, me gustaría completarla si el usuario presionó el botón de inicio.
Entonces, ¿es aceptado / una buena práctica envolver cada transacción de red (y no estoy hablando de descargar una gran cantidad de datos, en su mayoría un XML corto) beginBackgroundTaskWithExpirationHandler
para estar seguro?
Respuestas:
Si desea que su transacción de red continúe en segundo plano, deberá envolverla en una tarea en segundo plano. También es muy importante que llame
endBackgroundTask
cuando haya terminado; de lo contrario, la aplicación se eliminará una vez que haya expirado el tiempo asignado.Los míos tienden a verse así:
Tengo una
UIBackgroundTaskIdentifier
propiedad para cada tarea en segundo plano.Código equivalente en Swift
fuente
La respuesta aceptada es muy útil y debería estar bien en la mayoría de los casos, sin embargo, me molestaron dos cosas:
Como han señalado varias personas, almacenar el identificador de la tarea como una propiedad significa que se puede sobrescribir si el método se llama varias veces, lo que lleva a una tarea que nunca terminará correctamente hasta que el sistema operativo la obligue a finalizar en el momento de vencimiento. .
Este patrón requiere una propiedad única para cada llamada, lo
beginBackgroundTaskWithExpirationHandler
que parece engorroso si tiene una aplicación más grande con muchos métodos de red.Para resolver estos problemas, escribí un singleton que se encarga de todas las tuberías y rastrea las tareas activas en un diccionario. No se necesitan propiedades para realizar un seguimiento de los identificadores de tareas. Parece funcionar bien. El uso se simplifica a:
De manera opcional, si desea proporcionar un bloque de finalización que haga algo más que finalizar la tarea (que está integrada), puede llamar a:
El código fuente relevante está disponible a continuación (el material singleton se excluye por brevedad). Comentarios / retroalimentación bienvenidos.
fuente
typedef
CompletionBlock? Simplemente esto:typedef void (^CompletionBlock)();
Aquí hay una clase Swift que encapsula la ejecución de una tarea en segundo plano:
La forma más sencilla de utilizarlo:
Si necesita esperar una devolución de llamada de delegado antes de finalizar, use algo como esto:
fuente
begin
método, pero es fácil ver cómo agregar esa característica.Como se indica aquí y en las respuestas a otras preguntas de SO, NO desea usar
beginBackgroundTask
solo cuando su aplicación pasará a segundo plano; por el contrario, se debe utilizar una tarea de fondo para cualquier operación que consume tiempo cuya finalización desea asegurarse incluso si la aplicación no entra en el fondo.Por lo tanto, es probable que su código termine salpicado de repeticiones del mismo código repetitivo para llamar
beginBackgroundTask
y de formaendBackgroundTask
coherente. Para evitar esta repetición, es ciertamente razonable querer empaquetar el texto estándar en una sola entidad encapsulada.Me gustan algunas de las respuestas existentes para hacer eso, pero creo que la mejor manera es usar una subclase de Operación:
Puede poner la operación en cola en cualquier OperationQueue y manipular esa cola como mejor le parezca. Por ejemplo, puede cancelar prematuramente cualquier operación existente en la cola.
Si tiene más de una cosa que hacer, puede encadenar varias operaciones de tareas en segundo plano. Dependencias de soporte de operaciones.
Operation Queue puede (y debe) ser una cola en segundo plano; por lo tanto, no hay necesidad de preocuparse por realizar código asincrónico dentro de su tarea, porque la Operación es el código asincrónico. (De hecho, no tiene sentido ejecutar otro nivel de código asincrónico dentro de una Operación, ya que la Operación terminaría antes de que ese código pudiera siquiera comenzar. Si necesitara hacer eso, usaría otra Operación).
Aquí hay una posible subclase de Operación:
Debería ser obvio cómo usar esto, pero en caso de que no lo sea, imagine que tenemos una OperationQueue global:
Entonces, para un lote de código típico que consume mucho tiempo, diríamos:
Si su lote de código que consume mucho tiempo se puede dividir en etapas, es posible que desee retirarse antes si se cancela su tarea. En ese caso, simplemente regrese prematuramente del cierre. Tenga en cuenta que su referencia a la tarea desde dentro del cierre debe ser débil o obtendrá un ciclo de retención. Aquí hay una ilustración artificial:
En caso de que tenga que hacer una limpieza en caso de que la tarea en segundo plano se cancele prematuramente, proporcioné una
cleanup
propiedad de controlador opcional (no se usa en los ejemplos anteriores). Algunas otras respuestas fueron criticadas por no incluir eso.fuente
Implementé la solución de Joel. Aquí está el código completo:
Archivo .h:
Archivo .m:
fuente