Copiei um array para uma nova variável. Alterei apenas a cópia, mas o original também mudou. É um mistério que todo programador encontra pelo menos uma vez.
O culpado são as duas faces da palavra “cópia”. Uma cópia duplica o valor inteiro; a outra apenas cria mais um rótulo apontando para os mesmos dados.
Vamos esclarecer a diferença entre cópia rasa e profunda e ver como o Swift muda esse cenário. Tipos por valor e Copy-on-Write ajudam, mas as armadilhas continuam.
Este é o resumo principal.
Aplicar essa distinção a padrões de criação de objetos leva a Padrão Prototype do Swift e NSCopying; já a implementação que adia a cópia real por desempenho é detalhada em Swift Copy-on-Write.
- Cópia por referência: apenas o rótulo que aponta para os dados é duplicado. Alterar um lado altera o outro
- Cópia rasa: a camada externa é nova, mas as referências internas continuam apontando para os mesmos objetos
- Cópia profunda: todo o conteúdo é recriado recursivamente. As cópias ficam totalmente independentes
- Tipos por valor do Swift se comportam como cópias independentes na atribuição, mas, se contiverem uma classe, o problema da cópia rasa volta a aparecer
É preciso separar três níveis
É comum falar apenas em cópia rasa e profunda, mas o quadro só fica correto quando a cópia por referência é incluída como um terceiro nível.
Cópia por referência não é cópia; é dar um alias. Ao atribuir uma instância de classe a outra variável, existe um único objeto e duas referências apontando para ele.
class Profile { var name = "Desenvolvedor Kim" }
let a = Profile()
let b = a // Cópia por referência: um objeto
b.name = "Programador Lee"
print(a.name) // "Programador Lee” — o original também muda
Cópia rasa cria apenas uma nova camada externa. Se o conteúdo for composto por referências, os objetos apontados continuam compartilhados.
Cópia profunda recria a caixa, as caixas dentro dela e tudo que estiver abaixo, até o fim. As duas estruturas são totalmente independentes; alterar uma não afeta a outra.
Tipos por valor no Swift: atribuição é cópia
Structs, enums e tipos básicos do Swift são tipos por valor; por isso, o valor é duplicado no momento da atribuição.
var original = [1, 2, 3]
var copy = original
copy.append(4)
print(original) // [1, 2, 3] — Seguro
Isso é bem diferente de linguagens em que atribuir um array faz uma cópia por referência. No Python, atribuir uma lista faz com que as duas variáveis apontem para a mesma lista.
“Copiar tudo sempre não seria lento?” É aqui que entra o Copy-on-Write (COW). Array, Dictionary e String compartilham o buffer interno no momento da atribuição e só fazem a cópia real quando um dos lados é alterado. Ler não tem custo de cópia e, semanticamente, os valores sempre se comportam como independentes. É um design que combina a segurança dos tipos por valor com a eficiência do compartilhamento de referências.
Mas as armadilhas continuam
Assim que um tipo por valor contém um tipo por referência, o problema da cópia rasa reaparece.
class Attachment { var filename = "a.png" }
struct Mail {
var title: String
var attachment: Attachment // Uma classe dentro de uma struct
}
var mail1 = Mail(title: "Original", attachment: Attachment())
var mail2 = mail1 // A struct é copiada, mas...
mail2.attachment.filename = "b.png"
print(mail1.attachment.filename) // "b.png" — O original também muda!
A struct Mail foi copiada de fato. title é independente. Porém, attachment continha uma “referência”, então o que foi copiado também é uma referência. Os dois Mail passam a compartilhar o mesmo Attachment. Essa é exatamente a definição de cópia rasa.
Para resolver isso, é preciso criar uma cópia profunda manualmente. Você pode escrever uma lógica que crie um novo Attachment ou transformar Attachment em uma struct desde o início. Na família Objective-C, a abordagem tradicional é implementar copy(with:) de NSCopying; se uma classe contiver outra classe, ela também deverá ser copiada recursivamente para obter uma cópia profunda de verdade.
Na prática, a conclusão coincide com o motivo pelo qual o Swift recomenda tipos por valor. Se um modelo for composto apenas por structs e propriedades de tipos por valor, o problema desaparece.
Em uma frase para entrevistas
“A cópia rasa cria apenas uma nova camada externa de nível superior e compartilha as referências internas; a cópia profunda duplica recursivamente o conteúdo interno e torna a cópia totalmente independente. Tipos por valor do Swift se comportam como cópias independentes na atribuição, mas propriedades de tipos por referência dentro deles continuam compartilhadas, então é preciso ter cuidado.”
Para perguntas complementares, basta responder “O que é COW?” (uma otimização que adia a cópia até a alteração) e “Como implementar uma cópia profunda?” (NSCopying ou duplicação manual recursiva).
Resumo
- Entenda a cópia em três níveis: cópia por referência → cópia rasa → cópia profunda
- A cópia por referência duplica apenas o rótulo (objeto compartilhado); a cópia rasa duplica apenas a camada externa (referências internas compartilhadas); a cópia profunda duplica tudo (totalmente independente)
- Nos tipos por valor do Swift, atribuição é cópia; Array e String adiam a cópia real até a alteração usando COW
- Se um tipo por valor tiver uma propriedade de classe, o problema da cópia rasa será reproduzido integralmente
- Quando uma cópia profunda for necessária, implemente NSCopying ou uma lógica manual de duplicação recursiva
- A solução fundamental é construir modelos com tipos por valor — outro motivo para o Swift recomendar structs

