por Payal Gupta

copiar um objeto sempre foi uma parte essencial do paradigma de codificação. Seja em Swift, Objective-C, JAVA ou qualquer outra linguagem, sempre precisaremos copiar um objeto para uso em diferentes contextos.

neste artigo, discutiremos em detalhes como copiar diferentes tipos de dados no Swift e como eles se comportam em diferentes circunstâncias.

tipos de valor e Referência

todos os tipos de dados no Swift se enquadram amplamente em duas categorias, a saber, tipos de valor e tipos de referência.

  • tipo de Valor — cada instância mantém uma cópia exclusiva de seus dados. Os tipos de dados que se enquadram nesta categoria incluem — all the basic data types, struct, enum, array, tuples.
  • tipo de Referência — as instâncias compartilham uma única cópia dos dados e o tipo geralmente é definido como class.

a característica mais distintiva de ambos os tipos reside no seu comportamento de cópia.

o que é cópia profunda e superficial?

Um exemplo, se se trata de um tipo de valor ou um tipo de referência, podem ser copiados em uma das seguintes formas:

cópia de Profundidade — Duplicatas tudo

  • Com uma cópia profunda, qualquer objeto apontado pela fonte é copiado e a cópia é apontado pelo destino. Então, dois objetos completamente separados serão criados.
  • Coleções-uma cópia profunda de uma coleção é duas coleções com todos os elementos da coleção original duplicados.
  • menos propenso a condições de corrida e tem um bom desempenho em um ambiente multithread — as mudanças em um objeto não terão efeito em outro objeto.
  • os tipos de valor são copiados profundamente.

No código acima,

  • Linha 1: arr1 array (um tipo de valor) de Seqüências de caracteres
  • Linha 2: arr1 é atribuído a arr2. Isto irá criar uma cópia profunda de arr1 e, em seguida, atribuir a cópia arr2
  • Linhas 7 a 11: quaisquer alterações feitas em arr2 não refletem em arr1 .

é isso que é deep copy-instâncias completamente separadas. O mesmo conceito funciona com todos os tipos de valor.

em alguns cenários, ou seja, quando um tipo de valor contém tipos de referência aninhados, o deep copy revela um tipo diferente de comportamento. Veremos isso nas próximas seções.

cópia superficial — duplica o mínimo possível

  • com uma cópia superficial, qualquer objeto apontado pela fonte também é apontado pelo destino. Portanto, apenas um objeto será criado na memória.
  • Coleções-uma cópia superficial de uma coleção é uma cópia da estrutura da coleção, não dos elementos. Com uma cópia superficial, duas coleções agora compartilham os elementos individuais.
  • mais rápido – apenas a referência é copiada.
  • copiar tipos de referência cria uma cópia superficial.

No código acima,

  • Linhas 1 a 8: tipo
  • Linha 10: a1 — uma instância de Address tipo
  • Linha 11: a1 é atribuído a a2. Isso criará uma cópia superficial de a1 e , em seguida, atribuirá essa cópia a a2, ou seja, apenas a referência é copiada para a2.
  • linhas 16 a 19: qualquer alteração feita em a2 certamente refletirá em a1.

na ilustração acima, podemos ver que ambos a1 e a2 apontam para o mesmo endereço de memória.

copiando tipos de referência profundamente

a partir de agora, sabemos que sempre que tentamos copiar um tipo de referência, apenas a referência ao objeto é copiada. Nenhum novo objeto é criado. E se quisermos criar um objeto completamente separado?

podemos criar uma cópia profunda do tipo de referência usando o método copy(). De acordo com a documentação,

copy() — retorna o objeto retornado por copy(with:).

este é um método de conveniência para classes que adotam o protocolo NSCopying. Uma exceção é levantada se não houver implementação para copy(with:).

vamos reestruturar o Address class que criamos no trecho de código 2 para estar em conformidade com o protocolo NSCopying.

No código acima,

  • Linhas de 1 a 14: Address tipo de classe a está em conformidade com NSCopying e implementa copy(with:) método
  • Linha 16: a1 — uma instância de Address tipo
  • Linha 17: a1 é atribuído a a2 usando copy() método. Isso criará uma cópia profunda de a1 e , em seguida, atribuirá essa cópia a a2, ou seja, um objeto completamente novo será criado.
  • linhas 22 a 25: quaisquer alterações feitas em a2 não refletirão em a1 .

como é evidente na ilustração acima, tanto a1 quanto a2 apontam para diferentes locais de memória.

vejamos outro exemplo. Desta vez, veremos como funciona com tipos de referência aninhados — um tipo de referência contendo outro tipo de referência.

no código acima,

  • linha 22: uma cópia profunda de p1 é atribuída a p2 usando o método copy(). Isso implica que qualquer mudança em um deles não deve ter nenhum efeito sobre o outro.
  • as linhas 27 a 28: p2's name e city os valores são alterados. Estes não devem refletir em p1.
  • linha 30: p1's name é como esperado, mas é city? Deve ser "Mumbai" não deveria? Mas não podemos ver isso acontecer. "Bangalore" foi apenas para p2 certo? Sim … exactamente.?

cópia profunda…!? Isso não era esperado de você. Disseste que ias copiar tudo. E agora você está se comportando assim. Por que oh por quê..?! O que faço agora? ☠️

não entre em pânico. Vamos ver o que os endereços de memória têm a dizer nisso.

a Partir da ilustração acima, podemos ver que

  • p1 e p2 apontar para diferentes localizações de memória como o esperado.
  • mas suas variáveis address ainda estão apontando para o mesmo local. Isso significa que, mesmo depois de copiá — los profundamente, apenas as referências são copiadas-ou seja, uma cópia superficial, é claro.

por favor note: toda vez que copiamos um tipo de referência, uma cópia superficial é criada por padrão até especificarmos explicitamente que ela deve ser copiada profundamente.

func copy(with zone: NSZone? = nil) -> Any{ let person = Person(self.name, self.address) return person}

no método acima que implementamos anteriormente para a classe Person, criamos uma nova instância copiando o endereço com self.address. Isso só copiará a referência ao objeto address. Esta é a razão pela qual ambos p1 e p2's address apontam para o mesmo local.Portanto, copiar o objeto usando o método copy() não criará uma cópia profunda verdadeira do objeto.

para duplicar um objeto de referência completamente: o tipo de referência, juntamente com todos os tipos de referência aninhados, deve ser copiado com o método copy().

let person = Person(self.name, self.address.copy() as? Address)

usando o código acima no func copy(with zone: NSZone? = nil) -> qualquer método fará tudo funcionar. Você pode ver isso na ilustração abaixo.

True Deep Copy-tipos de referência e valor

já vimos como podemos criar uma cópia profunda dos tipos de referência. Claro que podemos fazer isso com todos os tipos de referência aninhados.

mas e o tipo de referência aninhado em um tipo de valor, que é uma matriz de objetos, ou uma variável de tipo de referência em uma estrutura ou talvez uma tupla? Podemos resolver isso usando copy() também? Não, Não podemos, na verdade. O método copy() requer a implementação do protocolo NSCopying, que funciona apenas para subclasses NSObject. Os tipos de valor não suportam herança, portanto, não podemos usar copy() com eles.

na linha 2, apenas a estrutura de arr1 é profundamente copiada, mas os objetos Address dentro dela ainda são copiados rasos. Você pode ver isso no mapa de memória abaixo.

os elementos em arr1 e arr2 apontam para os mesmos locais de memória. Isso ocorre por causa do mesmo motivo — os tipos de referência são copiados rasos por padrão.

serializar e, em seguida, desserializar um objeto sempre cria um novo objeto. É válido para ambos os tipos de valor, bem como os tipos de referência.

Aqui estão algumas APIs que podemos usar para serializar e desserializar dados:

  1. NSCoding — Um protocolo que permite que um objeto seja codificado e decodificado para arquivamento e distribuição. Ele só funcionará com objetos do tipo class, pois requer herança de NSObject.
  2. Codable – torne seus tipos de dados codificáveis e decodificáveis para compatibilidade com representações externas, como JSON. Ele funcionará para ambos os tipos de valor – struct, array, tuple, basic data typesbem como tipos de referência — class .

vamos reestruturar a classe Address um pouco mais para estar em conformidade com o protocolo Codable e remover todo o código NSCopying que adicionamos anteriormente no trecho de código 3.

no código acima, as linhas 11-13 criarão uma cópia profunda verdadeira de arr1. Abaixo está a ilustração que dá uma imagem clara dos locais de memória.

Copiar na gravação

Copiar na gravação é uma técnica de otimização que ajuda a aumentar o desempenho ao copiar tipos de valor.Digamos que copiemos uma única String ou Int ou talvez qualquer outro tipo de valor — não enfrentaremos nenhum problema de desempenho crucial nesse caso. Mas e quando copiamos uma matriz de milhares de elementos? Ainda não criará problemas de desempenho? E se apenas copiarmos e não fizermos alterações nessa cópia? Não é essa memória extra que usamos apenas um desperdício nesse caso?

aqui vem o conceito de cópia em gravação — ao copiar, cada referência aponta para o mesmo endereço de memória. É somente quando uma das referências modifica os dados subjacentes que Swift realmente copia a instância original e faz a modificação.

ou seja, seja uma cópia profunda ou uma cópia superficial, uma nova cópia não será criada até que façamos uma alteração em um dos objetos.

No código acima,

  • Linha 2: uma profunda cópia de arr1 é atribuído a arr2
  • Linhas 4 e 5: arr1 e arr2 ainda apontar para o mesmo endereço de memória
  • Linha 7: as alterações feitas em arr2
  • Linhas 9 e 10: arr1 e arr2 agora apontando para diferentes localizações de memória

Agora você sabe mais sobre profunda e superficial cópias e como eles se comportam em diferentes cenários com diferentes tipos de dados. Você pode experimentá-los com seu próprio conjunto de exemplos e ver quais resultados você obtém.

Leitura adicional

não se esqueça de ler meus outros artigos:

  1. Tudo sobre Codificável em Swift 4
  2. Tudo o que você sempre quis saber sobre notificações no iOS
  3. Cor com GRADIENTES iOS
  4. Codificação para iOS 11: Como arrastar & drop em coleções & tabelas
  5. Tudo o que você precisa saber sobre Extensões de Hoje (Widget) no iOS 10
  6. UICollectionViewCell seleção fácil..!!

Deixe uma resposta

O seu endereço de email não será publicado.