Introdução à concorrência e multithreading em Swift
Os aplicativos modernos para iOS e macOS devem ser rápidos e responsivos. O usuário não deve esperar enquanto uma imagem é carregada ou um cálculo complexo é executado. Para isso, o Swift possui mecanismos de concorrência e multithreading. Neste artigo, vamos explorar os conceitos-chave, desde o clássico Grand Central Dispatch (GCD) até o moderno async/await do Swift Concurrency.
Concorrência (concurrency) é a capacidade de um programa executar várias tarefas simultaneamente, alternando entre elas. Multithreading é uma das implementações de concorrência, onde as tarefas são executadas em diferentes threads do sistema operacional. O Swift fornece ferramentas poderosas e seguras para trabalhar com esses conceitos.
1. Grand Central Dispatch (GCD) — a base do multithreading
Grand Central Dispatch (GCD) é uma biblioteca de baixo nível da Apple para gerenciar a execução paralela de código. Ela funciona com base em filas (dispatch queues).
Tipos de filas no GCD
- Serial Queue (fila serial) — as tarefas são executadas uma após a outra, na ordem em que foram adicionadas.
- Concurrent Queue (fila concorrente) — as tarefas podem ser executadas simultaneamente em diferentes threads.
- Main Queue (fila principal) — executa tarefas na thread principal, destinada a atualizações da interface do usuário (UI).
- Global Queues (filas globais) — filas concorrentes do sistema com diferentes prioridades (QoS).
Exemplo: executar uma tarefa em segundo plano e atualizar a UI
import UIKit
class ViewController: UIViewController { @IBOutlet weak var imageView: UIImageView!
func loadImage(from url: URL) { // Mudamos para a fila de segundo plano DispatchQueue.global(qos: .background).async { [weak self] in guard let data = try? Data(contentsOf: url), let image = UIImage(data: data) else { return } // Voltamos para a thread principal para atualizar a UI DispatchQueue.main.async { self?.imageView.image = image } } }}Neste exemplo, baixamos a imagem em uma thread de segundo plano para não bloquear a interface e, em seguida, atualizamos a UI na thread principal.
DispatchGroup para sincronização de tarefas
DispatchGroup permite monitorar a execução de um grupo de tarefas assíncronas e executar uma ação após sua conclusão.
let group = DispatchGroup()
group.enter()fetchData1 { result in print("Dados 1 recebidos") group.leave()}
group.enter()fetchData2 { result in print("Dados 2 recebidos") group.leave()}
group.notify(queue: .main) { print("Todos os dados carregados, atualizando a UI")}2. OperationQueue — abordagem orientada a objetos
OperationQueue é uma abstração de nível mais alto sobre o GCD. Ela trabalha com objetos Operation, que representam uma unidade de trabalho.
Vantagens da OperationQueue
- Possibilidade de cancelar tarefas (cancel).
- Definição de dependências entre operações (addDependency).
- Monitoramento do estado da operação (isFinished, isExecuting, isCancelled).
- Configuração do número máximo de operações paralelas.
Exemplo: carregar várias imagens
import UIKit
class ImageLoader { private let queue = OperationQueue() func loadImages(from urls: [URL], completion: @escaping ([UIImage]) -> Void) { var images: [UIImage] = [] let completionOperation = BlockOperation { DispatchQueue.main.async { completion(images) } } for url in urls { let o
```