Estou um pouco confuso sobre como e quando usar beginBackgroundTaskWithExpirationHandler
.
A Apple mostra em seus exemplos como usá-lo em applicationDidEnterBackground
delegação, para obter mais tempo para concluir alguma tarefa importante, geralmente uma transação de rede.
Ao olhar em meu aplicativo, parece que a maioria das coisas da minha rede é importante e, quando um for iniciado, gostaria de concluí-lo se o usuário pressionasse o botão home.
Portanto, é aceitável / boa prática agrupar todas as transações de rede (e não estou falando sobre o download de grandes blocos de dados, principalmente alguns xml curtos) beginBackgroundTaskWithExpirationHandler
para ficar no lado seguro?
Respostas:
Se você deseja que sua transação de rede continue em segundo plano, será necessário envolvê-la em uma tarefa em segundo plano. Também é muito importante que você ligue
endBackgroundTask
quando terminar - caso contrário, o aplicativo será encerrado após o tempo alocado expirar.Os meus tendem a ser mais ou menos assim:
Eu tenho uma
UIBackgroundTaskIdentifier
propriedade para cada tarefa em segundo planoCódigo equivalente em Swift
fonte
A resposta aceita é muito útil e deve ser adequada na maioria dos casos, no entanto, duas coisas me incomodaram:
Como várias pessoas notaram, armazenar o identificador de tarefa como uma propriedade significa que ele pode ser sobrescrito se o método for chamado várias vezes, levando a uma tarefa que nunca será encerrada normalmente até que seja forçada pelo sistema operacional na expiração do tempo .
Esse padrão requer uma propriedade exclusiva para cada chamada, o
beginBackgroundTaskWithExpirationHandler
que parece complicado se você tiver um aplicativo maior com muitos métodos de rede.Para resolver esses problemas, escrevi um singleton que cuida de todo o encanamento e rastreia as tarefas ativas em um dicionário. Nenhuma propriedade necessária para controlar os identificadores de tarefa. Parece funcionar bem. O uso é simplificado para:
Opcionalmente, se você deseja fornecer um bloco de conclusão que faz algo além de encerrar a tarefa (que está embutido), você pode chamar:
Código-fonte relevante disponível abaixo (itens singleton excluídos por questões de brevidade). Comentários / feedback bem-vindos.
fonte
typedef
CompletionBlock? Simplesmente isto:typedef void (^CompletionBlock)();
Aqui está uma classe Swift que encapsula a execução de uma tarefa em segundo plano:
A maneira mais simples de usar:
Se você precisar esperar por um retorno de chamada de delegado antes de encerrar, use algo assim:
fonte
begin
método, mas é fácil ver como adicionar esse recurso.Conforme observado aqui e em respostas a outras perguntas do SO, você NÃO deseja usar
beginBackgroundTask
apenas quando seu aplicativo ficará em segundo plano; pelo contrário, você deve usar uma tarefa de fundo para qualquer operação demorada cuja conclusão você quiser garantir, mesmo que o aplicativo faz ir para o fundo.Portanto, é provável que seu código acabe salpicado de repetições do mesmo código clichê para chamar
beginBackgroundTask
eendBackgroundTask
coerente. Para evitar essa repetição, é certamente razoável querer empacotar o clichê em uma única entidade encapsulada.Gosto de algumas das respostas existentes para fazer isso, mas acho que a melhor maneira é usar uma subclasse de Operação:
Você pode enfileirar a operação em qualquer OperationQueue e manipular essa fila como achar melhor. Por exemplo, você está livre para cancelar prematuramente qualquer operação existente na fila.
Se você tem mais de uma coisa a fazer, pode encadear várias operações de tarefas em segundo plano. Dependências de suporte de operações.
A fila de operações pode (e deve) ser uma fila de segundo plano; assim, não há necessidade de se preocupar em realizar código assíncrono dentro de sua tarefa, pois a Operação é o código assíncrono. (Na verdade, não faz sentido executar outro nível de código assíncrono dentro de uma operação, pois a operação terminaria antes mesmo que o código pudesse começar. Se você precisasse fazer isso, usaria outra operação.)
Aqui está uma possível subclasse de Operação:
Deve ser óbvio como usar isso, mas caso não seja, imagine que temos uma OperationQueue global:
Portanto, para um lote típico de código demorado, diríamos:
Se seu lote de código demorado pode ser dividido em estágios, convém encerrar mais cedo se a tarefa for cancelada. Nesse caso, basta retornar prematuramente do fechamento. Observe que sua referência à tarefa de dentro do fechamento precisa ser fraca ou você obterá um ciclo de retenção. Aqui está uma ilustração artificial:
Caso você tenha que fazer uma limpeza, caso a própria tarefa em segundo plano seja cancelada prematuramente, forneci uma
cleanup
propriedade de manipulador opcional (não usada nos exemplos anteriores). Algumas outras respostas foram criticadas por não incluir isso.fonte
Implementei a solução do Joel. Aqui está o código completo:
arquivo .h:
arquivo .m:
fonte