Swift e Objective-C

Swift Copy-on-Write: como tipos por valor substituem o padrão Prototype (Resumo)

O Swift Copy-on-Write compartilha o armazenamento quando tipos por valor são atribuídos e só faz uma cópia real quando um dos lados é modificado. Este texto explica como essa otimização preserva a semântica de valor e reduz o papel do padrão Prototype.

5 min de leitura
Imagem de capa de Swift Copy-on-Write: como tipos por valor substituem o padrão Prototype (Resumo)

Quando você aprende Objective-C ou Java primeiro e depois migra para Swift, há um ponto que certamente vai encontrar.

É a pergunta: “Como clonar um objeto?”

É fácil sair procurando um método como clone(), mas no Swift quase nunca é necessário.

Vou começar pela conclusão.

Graças a tipos por valor, como structs e arrays, e à otimização Copy-on-Write (cópia adiada), o Swift substitui, no nível da linguagem, o padrão Prototype dos padrões de projeto.

Você não precisa criar um padrão separado para clonar objetos; basta atribuir o valor para obter uma cópia segura.

Neste artigo, vamos entender o que o padrão Prototype pretendia resolver originalmente e como os tipos por valor e o Copy-on-Write do Swift incorporam isso naturalmente, com exemplos.

Para distinguir os tipos de cópia, Cópia rasa vs. cópia profunda é a base. Quando também é necessário clonar classes, Padrão Prototype e NSCopying no Swift mostra a alternativa.


O problema que o padrão Prototype pretendia resolver

O padrão Prototype pertence aos padrões criacionais de GoF.

A ideia é simples: em vez de criar um objeto novo do zero, você clona um objeto existente para obter uma nova instância.

Por que isso era necessário?

Em linguagens como Objective-C e Java, os objetos são principalmente tipos por referência.

Ao atribuir um objeto a uma variável, você copia apenas o endereço que aponta para o mesmo objeto, não o valor.

Por isso, ao alterar A, B também muda.

Para evitar isso, os desenvolvedores criavam métodos como clone() ou copy() para retornar uma “cópia verdadeira”.

O padrão Prototype é uma ferramenta para esse tipo de situação.

  • Quando criar objetos é caro e você precisa de vários objetos semelhantes
  • Quando precisa de uma cópia independente sem alterar o original
  • Quando quer que o próprio objeto seja responsável pela lógica de clonagem

Tipos por valor no Swift: basta atribuir para copiar

Structs e enums do Swift, além de tipos padrão como Array, Dictionary e String, são todos tipos por valor.

Tipos por valor são copiados quando atribuídos ou passados para uma função.

Em outras palavras, a linguagem cria a cópia automaticamente.

O exemplo deixa isso muito mais claro.

struct Point { var x: Int; var y: Int }

var a = Point(x: 1, y: 2)
var b = a      // o valor é copiado neste momento
b.x = 99
// a.xcontinua 1, b.xapenas 99

b = aEsta linha substitui o trabalho que o clone() do padrão Prototype fazia.

Não é necessário criar um método de clonagem separado nem adotar um protocolo de cópia.

O original a fica protegido com segurança, e b se torna uma cópia totalmente independente.

Os bugs complicados causados por objetos interligados nem podem ocorrer nessa estrutura.


Como o Copy-on-Write resolve o problema de desempenho

Aqui surge uma preocupação natural.

“Se copiarmos tudo a cada atribuição, arrays grandes não ficarão lentos demais?”

É uma observação válida. Por isso o Swift usa Copy-on-Write, ou CoW.

O princípio do CoW é o seguinte.

Ao atribuir um valor, o armazenamento interno é compartilhado primeiro. A cópia real dos dados é adiada.

Quando um dos dois tenta modificar o valor, a cópia real finalmente acontece.

Compartilhado até modificar; separado no momento da modificação
Compartilhado até modificar; separado no momento da modificação
Momento Operação interna Custo
Ao atribuir Armazenamento compartilhado (apenas referência) Muito baixo
Ao apenas ler O compartilhamento continua Sem cópia
Ao modificar o valor A cópia real acontece aqui Ocorre somente neste momento

Assim, os desenvolvedores aproveitam a segurança dos tipos por valor sem pagar custos de cópia desnecessários.

Se você apenas ler e não modificar, nenhuma cópia será feita.

Depois que entendi esse conceito, passei a me preocupar muito menos com clonagem
Depois que entendi esse conceito, passei a me preocupar muito menos com clonagem

Tipos da biblioteca padrão, como Array, Dictionary, Set e String, já têm CoW integrado.

A linguagem e a biblioteca padrão cuidam disso automaticamente, sem que precisemos implementar nada.


Ainda existem casos em que o padrão Prototype é necessário?

“Então o padrão Prototype está completamente morto no Swift?”

Não necessariamente.

Há situações em que você precisa usar classes: quando a semântica de referência é indispensável, quando precisa de interoperabilidade com Objective-C ou quando a herança é necessária.

Nesses casos, se precisar de uma cópia independente de uma instância de classe, ainda terá de implementar a lógica de clonagem por conta própria.

O Swift oferece o protocolo NSCopying e copy(), que são essencialmente a forma tradicional do padrão Prototype.

Fica mais fácil pensar na divisão desta forma.

  • Usar structs e tipos por valor → A atribuição conclui a cópia; o padrão Prototype é desnecessário
  • É obrigatório usar classes → Implemente a lógica de clonagem quando necessário; é aqui que o padrão sobrevive

Por isso a comunidade Swift costuma recomendar: “Considere primeiro os tipos por valor”.

Quando tipos por valor são o padrão, o problema de clonagem desaparece.


Resumo

Tentar adaptar à força um padrão de projeto de uma linguagem desconhecida muitas vezes deixa o código mais complexo.

No Swift, basta lembrar que tipos por valor e Copy-on-Write ocupam naturalmente o lugar do padrão Prototype para tornar as preocupações com clonagem muito mais leves.

Na próxima vez que precisar copiar um objeto, antes de procurar clone(), pense: “Será que posso transformar isso em uma struct?”. Isso certamente ajudará.

Continue lendo