Magia do DispatchQueue.main.asyncAfter: como funciona o atraso no Swift 3

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: por que "esperar" não é tão simples quanto parece

\n\n

Imagine: você está escrevendo um aplicativo para iOS em Swift 3. Precisa mostrar uma animação e, depois de alguns segundos, ocultá-la. Ou enviar uma requisição ao servidor, mas não imediatamente, sim com um atraso. Parece simples, não? É só pegar e escrever sleep(2) — e pronto. Mas se você fizer isso, seu aplicativo vai congelar: a interface trava, o usuário começa a tocar na tela e, no final, você recebe uma avaliação negativa e perde reputação.

\n\n

A pergunta "How to program a delay in Swift 3" teve milhares de visualizações no Stack Overflow. E não é por acaso: iniciantes sempre pisam nos mesmos ancinhos. Neste artigo, não vamos apenas dar uma receita pronta. Vamos mergulhar por baixo dos panos do mecanismo de atrasos, entender como funciona o DispatchQueue.main.asyncAfter e compreender por que o sleep é um problema. E ainda vou mostrar um truque que vai economizar horas de depuração.

\n\n

Por que sleep() é um antipadrão no Swift

\n\n

Vamos logo esclarecer o equívoco mais comum. Muitos que vêm de outras linguagens (como Python com seu time.sleep()) tentam fazer assim:

\n\n
sleep(2) // bloqueia a thread por 2 segundos\nprint("Passaram-se 2 segundos")
\n\n

Esse código bloqueia a thread atual. Se você o chamar na thread principal (main thread) — pronto, o aplicativo "dorme" por 2 segundos. A interface do usuário não é atualizada, os botões não respondem, as animações não funcionam. O iOS simplesmente mata seu aplicativo se a thread principal ficar bloqueada por muito tempo (geralmente mais de 10 segundos).

\n\n

Além disso, sleep() não considera o estado do RunLoop. É uma chamada de sistema bruta que não permite que o sistema processe eventos normalmente. No Swift 3 (e posteriores), o caminho correto é usar o Grand Central Dispatch (GCD).

\n\n

DispatchQueue.main.asyncAfter: a maneira correta de criar um atraso

\n\n

A forma mais comum e recomendada de adiar a execução de código no Swift 3 é o método asyncAfter do GCD. Veja como ele é:

\n\n
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {\n    print("Passaram-se 2 segundos (modo não bloqueante)")\n}
\n\n

Aqui, dizemos ao sistema: "Daqui a 2 segundos a partir do momento atual, execute este bloco de código na thread principal". A thread principal permanece livre — as animações funcionam, os botões respondem, o usuário fica feliz.

\n\n

Mas vamos mais fundo. O que é deadline? Por que .now() + 2.0 e não apenas 2.0? E o que acontece "por baixo dos panos"?

\n\n

Anatomia do asyncAfter: deadline, DispatchTime e DispatchQoS

\n\n

asyncAfter aceita o parâmetro deadline do tipo DispatchTime. Não é apenas um número de milissegundos, mas um ponto absoluto no tempo, vinculado ao relógio do sistema. Quando você escreve .now() + 2.0, o GCD calcula: "hora atual + 2 segundos" e coloca a tarefa na fila com essa marca.

\n\n

Por baixo dos panos, o GCD usa mach_absolute_time() — um timer de hardware que não depende de alterações na hora do sistema. Ou seja, se o usuário atrasar o relógio, seu atraso não será afetado (ao contrário do Timer, que pode se comportar de forma imprevisível).

\n\n

Uma nuance importante: deadline é o tempo mínimo até a execução. O GCD não garante que o código será executado exatamente após 2 segundos. Se o sistema estiver ocupado (por exemplo, processando dados complexos), a execução pode atrasar. Para a maioria das tarefas de UI, isso não é crítico, mas para timers de alta precisão (como em jogos), é melhor usar CADisplayLink.

\n\n

Outro parâmetro é qos (Quality of Service). Você pode definir a prioridade da tarefa:

\n\n
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 1.0) {\n    // tarefa de alta prioridade\n}
\n\n

Blogs

Book Recommendations