Introdução: por que o perfilamento de código Swift é a habilidade nº 1 para desenvolvedores iOS/macOS
Os aplicativos modernos para iOS e macOS estão se tornando cada vez mais complexos, e os usuários, mais exigentes. Ninguém vai esperar 5 segundos para uma tela carregar ou tolerar animações travadas. A otimização de desempenho não é um luxo, mas uma necessidade. Mas como saber exatamente qual parte do código está deixando todo o aplicativo lento? A resposta é o perfilamento de código Swift.
O perfilamento é o processo de coleta de dados sobre a execução de um programa: tempo de execução de funções, uso de memória, frequência de chamadas. Sem ele, qualquer tentativa de otimização se torna um palpite no escuro. Neste artigo, vamos explorar ferramentas e técnicas práticas que ajudarão você a encontrar gargalos no código Swift para iOS e macOS. Você aprenderá a usar Instruments, os_signpost e as ferramentas integradas do Xcode, além de ver exemplos reais de código.
1. Ferramentas de perfilamento Swift no Xcode
O Xcode oferece um conjunto poderoso de ferramentas de perfilamento, agrupadas no aplicativo Instruments. Aqui estão as principais que serão úteis para todo desenvolvedor:
- Time Profiler — mostra quanto tempo de CPU é gasto em cada função. Ideal para encontrar "pontos críticos" (hot spots).
- Allocations — rastreia a alocação e liberação de memória. Ajuda a encontrar vazamentos e consumo excessivo.
- Leaks — procura automaticamente por vazamentos de memória (retain cycles).
- Core Animation — analisa o desempenho de gráficos e animações.
- os_signpost — API para adicionar manualmente marcadores no código, que ficam visíveis no Instruments.
Iniciar o perfilamento é simples: selecione o esquema (Scheme) no Xcode, pressione Cmd + I (Product → Profile) e escolha o modelo desejado no Instruments.
1.1. Primeira execução: Time Profiler
Suponha que você tenha um aplicativo que processa lentamente uma lista de 10.000 itens. Inicie o Time Profiler e execute o cenário. Após alguns segundos, você verá a pilha de chamadas ordenada por tempo. Veja como fica no código:
func processLargeArray() { let data = (0..<10000).map { $0 } let result = data.filter { $0 % 2 == 0 }.map { $0 * 2 } print(result.count)}O Time Profiler mostrará que filter e map consomem a maior parte do tempo. Mas isso é esperado. E se o problema for outro? Por exemplo, chamar frequentemente uma função custosa dentro de um loop.
2. Exemplo prático: otimizando o trabalho com Core Data
Um dos problemas comuns no iOS são consultas lentas ao Core Data. Vejamos um exemplo: carregar 5000 objetos com filtro por nome.
func fetchUsers(with name: String) -> [User] { let request = User.fetchRequest() request.predicate = NSPredicate(format: "name == %@", name) request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)] return (try? context.fetch(request)) ?? []}Ao executar o Time Profiler, você pode ver que o fetch leva 200 ms — muito. Mas por quê? Talvez o índice no campo name não tenha sido criado. No Xcode, adicione o índice no modelo de dados:
- Abra o arquivo .xcdatamodeld.
- Selecione a entidade
User. - No inspetor de dados (Data Model Inspector), adicione
namena seção Indexes.
Após isso, um novo perfil mostrará uma redução no tempo para 20–30 ms. Uma diferença de 10 vezes!
2.1. Usando os_signpost para marcação manual
Às vezes, as ferramentas padrão não fornecem o nível de detalhe necessário. É aí que o os_signpost entra em ação. Ele permite marcar manualmente o início e o fim de um trecho de código de interesse e, em seguida, ver essas marcações no Instruments.