paint-brush
Como usar RunLoop em aplicativos IOSpor@alekseimarinin
4,338 leituras
4,338 leituras

Como usar RunLoop em aplicativos IOS

por amarinin5m2024/03/04
Read on Terminal Reader

Muito longo; Para ler

Runloop é um loop que coordena o recebimento e processamento de eventos recebidos em um thread específico. Por padrão, o RunLoop principal está sempre rodando na aplicação; ele processa mensagens do sistema e as transmite para o aplicativo. Threads auxiliares requerem autodeterminação da necessidade de um RunLoop, e você mesmo terá que configurá-lo e executá-lo.
featured image - Como usar RunLoop em aplicativos IOS
amarinin HackerNoon profile picture

O que é um Runloop?

Um RunLoop é um loop que coordena o recebimento e o processamento de eventos recebidos em um thread específico.


O RunLoop está presente em todos os threads, mas por padrão está em modo de espera e não realiza nenhum trabalho.


O desenvolvedor pode executá-lo se necessário, mas não funcionará automaticamente. Para fazer isso, você precisará escrever código.

Que problema o Runloop resolve?

Em primeiro lugar, RunLoop foi projetado para gerenciar o fluxo de tarefas recebidas e executá-las no momento certo.


Isso é mais perceptível ao trabalhar com a UI, por exemplo, ao usar o UIScrollView.


Por padrão, o RunLoop principal está sempre rodando na aplicação; ele processa mensagens do sistema e as transmite para o aplicativo. Um exemplo de tais mensagens pode ser, por exemplo, um evento quando um usuário clica na tela.


Threads auxiliares requerem autodeterminação da necessidade de um RunLoop. Se precisar, você mesmo terá que configurá-lo e executá-lo. Executar RunLoop por padrão não é recomendado, é necessário apenas nos casos em que precisamos de interação ativa com threads.


Além disso, todos os temporizadores do aplicativo são executados no runloop, portanto, se você precisar interagir com eles em seu aplicativo, definitivamente precisará estudar os recursos do runloop.

Como funciona?

RunLoop é um loop e possui vários modos de operação que ajudam o desenvolvedor a entender quando executar uma tarefa específica.


Portanto, RunLoop pode estar nos seguintes modos:

  1. Default - O modo padrão, o fluxo é gratuito e grandes operações podem ser executadas com segurança nele.


  2. Tracking - O thread está ocupado fazendo algum trabalho importante. Neste ponto, é melhor não executar nenhuma tarefa, ou pelo menos executar algumas tarefas pequenas.


  3. Initialization - Este modo é executado uma vez durante a inicialização do fluxo.


  4. EventReceive - Este é um modo interno para receber eventos do sistema, que normalmente não é usado.

  5. Common - É um modo de espaço reservado que não tem significado prático.


    No RunLoop principal, esses modos são alternados automaticamente; o desenvolvedor pode usá-los para realizar tarefas demoradas para que o usuário não perceba a interface travada. Vejamos um exemplo.


O gerenciamento do ciclo de execução em outros RunLoop não é totalmente automático. Você precisa escrever código para um thread que iniciará o ciclo de execução no momento apropriado. Além disso, você precisa responder adequadamente aos eventos e usar loops infinitos para garantir que o ciclo de execução não pare.


Temos um UIScrollView e precisamos realizar uma tarefa grande no thread principal para que o usuário não perceba nada.


Podemos completar a tarefa da maneira usual:


 DispatchQueue.main.async { sleep(2) self.tableView.refreshControl?.endRefreshing() }


Mas o resultado será muito ruim. O usuário notará atrasos significativos na aplicação.

Esse efeito negativo ocorre devido ao fato de executarmos uma tarefa na thread principal sem prestar atenção ao que está acontecendo nela no momento.


Por conta disso, começamos a realizar nossa grande tarefa no momento em que o usuário interage com a interface. Isso, é claro, leva ao fato de o usuário ver a interface travada.


Isso pode ser evitado usando o mecanismo RunLoop. Vamos implementar a mesma lógica usando isto:


 CFRunLoopPerformBlock(CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue) { sleep(2) self.tableView.refreshControl?.endRefreshing() }


Deixe-me explicar o que acontece aqui. A função CFRunLoopPerformBlock adiciona código para execução por meio do RunLoop. Além do próprio bloco de código, esta função possui 2 parâmetros importantes.


O primeiro é responsável por selecionar qual RunLoop deverá executar a função. Neste exemplo, “principal” é usado.


O segundo é responsável pelo modo em que a tarefa será concluída.


Existem três modos possíveis no total:

  • Rastreamento - quando o usuário interage com a interface, como rolar por um UIScrollView.


  • Padrão – o usuário não interage com a interface. Neste momento, é possível concluir com segurança uma tarefa que consome muitos recursos.


  • Comum - combina o modo padrão e o modo de rastreamento.

    O resultado da execução do programa com o código acima será:

Quando o usuário começa a interagir com a interface do usuário (IU), o loop de execução principal muda para o modo "rastreamento" e suspende temporariamente o processamento de todos os outros eventos para garantir a suavidade da interface. Assim que o usuário para de interagir com a interface, o loop de execução retorna ao modo "padrão" e retoma a execução de nossa tarefa.

Temporizadores

Além da interface do usuário, o loop também está intimamente ligado ao funcionamento dos temporizadores.


Qualquer cronômetro no aplicativo é executado em loop e você precisa ter cuidado extra para não cometer erros ao trabalhar com eles, especialmente se eles forem responsáveis por funcionalidades importantes, como processamento de pagamentos.


 Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in // makeSomething }


O cronômetro inicia no modo padrão por padrão, portanto pode parar de funcionar se o usuário estiver percorrendo a tabela no momento. Isso ocorre porque o loop está atualmente no modo de rastreamento. É por isso que o código do exemplo pode não funcionar corretamente. Você pode corrigir esse problema adicionando um cronômetro no modo comum.


 let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in // makeSomething } RunLoop.main.add(timer, forMode: .common)


Além disso, os temporizadores podem não disparar no horário esperado ou nem disparar. Isso ocorre porque o RunLoop verifica os temporizadores apenas no início de cada ciclo. Se um cronômetro for acionado após o RunLoop passar por esse estágio, não saberemos disso até o início da próxima iteração. Ao mesmo tempo, quanto mais tempo a tarefa for executada no RunLoop, maior será o atraso.


Para resolver esse problema, você pode criar um novo thread, iniciar um RunLoop nesse thread e, em seguida, adicionar um timer ao thread sem adicionar nenhuma outra tarefa - desta forma, o timer funcionará corretamente.


 let thread = Thread { let timer = Timer(timeInterval: 1.0, repeats: true) { timer in // makeSomething } RunLoop.current.add(timer, forMode: .default) RunLoop.current.run() } thread.start()


O resultado

Neste artigo, vimos o que é RunLoop e quais problemas ele resolve em aplicativos iOS. Um RunLoop é um loop que coordena a recepção e o processamento de eventos recebidos dentro de um thread específico e possui vários modos de operação.


É especialmente útil ao trabalhar com a interface do usuário (UI) e temporizadores, pois tem a capacidade de executar tarefas no momento certo, o que permite evitar o "travamento" da interface e garantir o correto funcionamento dos temporizadores.


Apesar de trabalhar com Run Loop exigir codificação adicional, é um investimento que vale a pena que melhora a eficiência e a estabilidade da sua aplicação.