Como passar em uma entrevista de Swift: as principais perguntas de nível avançado
A entrevista para uma vaga de desenvolvedor iOS não é apenas um teste de conhecimento de UIKit e SwiftUI. No nível avançado, recrutadores e líderes técnicos querem ter certeza de que você entende como a memória funciona, a diferença entre class e struct no contexto de contagem de referências, como construir uma arquitetura corretamente usando protocolos e como gerenciar a assincronicidade. Neste artigo, analisaremos os tópicos principais que frequentemente aparecem em entrevistas de Swift de nível Pleno e Sênior.
1. Gerenciamento de memória e ARC (Automatic Reference Counting)
ARC é um dos tópicos mais comuns em entrevistas. Você precisa não apenas saber a teoria, mas também ser capaz de explicar como evitar vazamentos de memória.
1.1. Como o ARC funciona e quais ciclos de referências fortes existem?
O ARC conta automaticamente o número de referências fortes para um objeto de classe. Assim que o contador chega a zero, o objeto é removido da memória. O problema surge quando dois objetos se referenciam fortemente um ao outro — forma-se um ciclo (retain cycle).
class Parent { var child: Child?}
class Child { var parent: Parent? // referência forte de volta}
var parent: Parent? = Parent()var child: Child? = Child()parent?.child = childchild?.parent = parent// Ambos os objetos nunca serão liberados1.2. Como quebrar o ciclo?
Use weak ou unowned. weak é uma referência opcional que se torna automaticamente nil quando o objeto é removido. unowned é não opcional, mas pressupõe que o objeto viverá pelo menos tanto quanto o objeto que faz a referência.
class Child { weak var parent: Parent? // referência fraca}1.3. Como o ARC funciona com closures?
Closures capturam variáveis externas. Se uma closure é armazenada como uma propriedade de uma classe e dentro dela é usado self, um ciclo é formado. A solução é usar [weak self] ou [unowned self].
class NetworkManager { var completionHandler: (() -> Void)? func fetchData() { completionHandler = { [weak self] in guard let self = self else { return } self.processData() } } func processData() { }}2. Closures e escaping vs non-escaping
Entender a diferença entre closures escaping e non-escaping é um requisito obrigatório para qualquer posição avançada.
2.1. O que é uma closure escaping?
Uma closure é chamada de escaping se ela pode ser chamada após o retorno da função. Por padrão, todas as closures são non-escaping — o compilador sabe que elas serão executadas dentro do corpo da função. Para operações assíncronas (por exemplo, requisições de rede), é necessário especificar explicitamente @escaping.
func performAsyncTask(completion: @escaping (String) -> Void) { DispatchQueue.global().async { // Simulação de tarefa longa sleep(2) completion("Pronto") }}2.2. Por que closures non-escaping são mais rápidas?
O compilador otimiza closures non-escaping — ele pode não copiá-las para o heap, mas armazená-las na stack. Ele também pode evitar a contagem de referências, já que o tempo de vida da closure é limitado à chamada da função.
3. Genéricos e protocolos com tipos associados (PAT)
Genéricos são a base do Swift. Em uma entrevista, podem pedir para você escrever uma estrutura genérica ou explicar como funcionam os protocolos com tipos associados.
3.1. Escreva uma fila (Queue) genérica
struct Queue<T> { private var elements: [T] = [] mutating func enqueue(_ value: T) { elements.append(value) } mutating func dequeue() -> T? { guard !elements.isEmpty else { return nil }