Brincando com o Swift, vindo de Java, por que você escolheria um Struct em vez de uma classe? Parece que são a mesma coisa, com um Struct oferecendo menos funcionalidade. Por que escolher então?
swift
class
struct
design-principles
bluedevil2k
fonte
fonte
Respostas:
De acordo com a muito popular programação orientada a protocolos da WWDC 2015 em Swift ( vídeo , transcrição ), o Swift fornece vários recursos que tornam as estruturas melhores do que as classes em muitas circunstâncias.
Estruturas são preferíveis se forem relativamente pequenas e copiáveis, porque copiar é muito mais seguro do que ter várias referências à mesma instância que acontece com as classes. Isso é especialmente importante ao passar uma variável para muitas classes e / ou em um ambiente multithread. Se você sempre pode enviar uma cópia da sua variável para outros lugares, nunca precisa se preocupar com esse outro lugar, alterando o valor da sua variável abaixo de você.
Com o Structs, há muito menos necessidade de se preocupar com vazamentos de memória ou vários threads disparados para acessar / modificar uma única instância de uma variável. (Para os mais técnicos, a exceção é a captura de uma estrutura dentro de um fechamento, porque na verdade ela está capturando uma referência à instância, a menos que você a marque explicitamente para ser copiada).
As classes também podem ficar inchadas porque uma classe só pode herdar de uma única superclasse. Isso nos encoraja a criar superclasses enormes que abrangem muitas habilidades diferentes que são apenas vagamente relacionadas. O uso de protocolos, especialmente com extensões de protocolo onde você pode fornecer implementações para protocolos, permite eliminar a necessidade de classes para atingir esse tipo de comportamento.
A palestra apresenta esses cenários em que as aulas são preferidas:
Isso implica que as estruturas devem ser o padrão e as classes devem ser um substituto.
Por outro lado, a documentação da linguagem de programação Swift é um tanto contraditória:
Aqui está alegando que deveríamos usar classes e usar estruturas apenas em circunstâncias específicas. Por fim, você precisa entender a implicação no mundo real de tipos de valor versus tipos de referência e, em seguida, tomar uma decisão informada sobre quando usar estruturas ou classes. Além disso, lembre-se de que esses conceitos estão sempre evoluindo e a documentação da linguagem de programação Swift foi escrita antes da palestra sobre programação orientada a protocolo.
fonte
In practice, this means that most custom data constructs should be classes, not structures.
Você pode me explicar como, depois de ler isso, você entende que a maioria dos conjuntos de dados deve ser estruturas e não classes? Eles deram um conjunto específico de regras quando algo deveria ser uma estrutura e praticamente disseram "todos os outros cenários em que uma classe é melhor".Como as instâncias struct são alocadas na pilha e as instâncias de classe são alocadas na pilha, as estruturas às vezes podem ser drasticamente mais rápidas.
No entanto, você deve sempre medir por conta própria e decidir com base em seu caso de uso exclusivo.
Considere o exemplo a seguir, que demonstra duas estratégias de quebra de
Int
tipo de dados usandostruct
eclass
. Estou usando 10 valores repetidos para refletir melhor o mundo real, onde você tem vários campos.O desempenho é medido usando
O código pode ser encontrado em https://github.com/knguyen2708/StructVsClassPerformance
ATUALIZAÇÃO (27 de março de 2018) :
No Swift 4.0, Xcode 9.2, executando a versão compilada no iPhone 6S, iOS 11.2.6, a configuração do Swift Compiler é
-O -whole-module-optimization
:class
versão demorou 2,06 segundosstruct
versão demorou 4,17e-08 segundos (50.000.000 de vezes mais rápido)(Já não tenho média de várias execuções, pois as variações são muito pequenas, abaixo de 5%)
Nota : a diferença é muito menos dramática sem a otimização do módulo inteiro. Ficaria feliz se alguém pudesse apontar o que a bandeira realmente faz.
ATUALIZAÇÃO (7 de maio de 2016) :
A partir do Swift 2.2.1, Xcode 7.3, executando a versão compilada no iPhone 6S, iOS 9.3.1, com média de 5 execuções, a configuração do Swift Compiler é
-O -whole-module-optimization
:class
versão levou 2.159942142sstruct
versão levou 5.83E-08s (37.000.000 vezes mais rápido)Nota : como alguém mencionou que, em cenários do mundo real, provavelmente haverá mais de um campo em uma estrutura, adicionei testes para estruturas / classes com 10 campos em vez de 1. Surpreendentemente, os resultados não variam muito.
RESULTADOS ORIGINAIS (1 de junho de 2014):
(Executado em struct / class com 1 campo, não 10)
A partir do Swift 1.2, Xcode 6.3.2, executando a versão Build no iPhone 5S, iOS 8.3, em média, em 5 execuções
class
versão levou 9.788332333sstruct
versão levou 0.010532942s (900 vezes mais rápido)RESULTADOS ANTIGOS (de tempo desconhecido)
(Executado em struct / class com 1 campo, não 10)
Com a versão compilada no meu MacBook Pro:
class
versão demorou 1.10082 segstruct
versão levou 0,02324 s (50 vezes mais rápido)fonte
Semelhanças entre estruturas e classes.
Eu criei o gist para isso com exemplos simples. https://github.com/objc-swift/swift-classes-vs-structures
E diferenças
1. Herança.
estruturas não podem herdar rapidamente. Se você quiser
Vá para uma aula.
2. Passe por
As estruturas Swift passam por valor e as instâncias de classe passam por referência.
Diferenças contextuais
Constante e variáveis estruturais
Exemplo (usado na WWDC 2014)
Define uma estrutura chamada Point.
Agora, se eu tentar mudar o x. É uma expressão válida.
Mas se eu definisse um ponto como constante.
Nesse caso, o ponto inteiro é constante imutável.
Se eu usei uma classe Point, essa é uma expressão válida. Como em uma constante imutável de uma classe, a referência à própria classe não é suas variáveis de instância (a menos que essas variáveis sejam definidas como constantes)
fonte
Aqui estão alguns outros motivos a considerar:
As estruturas obtêm um inicializador automático que você não precisa manter no código.
Para obter isso em uma classe, você teria que adicionar o inicializador e manter o inicializador ...
Tipos básicos de coleção como
Array
são estruturas. Quanto mais você os usa em seu próprio código, mais você se acostuma a passar por valor e não por referência. Por exemplo:Aparentemente, imutabilidade versus mutabilidade é um tópico enorme, mas muitas pessoas inteligentes pensam que a imutabilidade - estruturas neste caso - é preferível. Objetos mutáveis vs imutáveis
fonte
internal
escopo.mutating
para ser explícito sobre quais funções alteram seu estado. Mas sua natureza como tipos de valor é o que é importante. Se você declarar uma estrutura,let
não poderá chamar nenhuma função mutante. O vídeo da WWDC 15 sobre Melhor programação através de tipos de valor é um excelente recurso.Supondo que sabemos que Struct é um tipo de valor e Class é um tipo de referência .
Se você não souber o que são um tipo de valor e um tipo de referência, consulte Qual é a diferença entre passar por referência e passar por valor?
Com base na publicação de mikeash :
Eu, pessoalmente, não nomeio minhas aulas assim. Eu costumo nomear meu UserManager em vez de UserController, mas a ideia é a mesma
Além disso, não use classe quando precisar substituir todas as instâncias de uma função, ou seja, elas não possuem nenhum compartilhamento funcionalidade .
Então, ao invés de ter várias subclasses de uma classe. Use várias estruturas que estejam em conformidade com um protocolo.
Outro caso razoável para estruturas é quando você deseja fazer um delta / diff do seu modelo antigo e novo. Com tipos de referências, você não pode fazer isso imediatamente. Com tipos de valor, as mutações não são compartilhadas.
fonte
Algumas vantagens:
fonte
A estrutura é muito mais rápida que a classe. Além disso, se você precisar de herança, deverá usar Classe. O ponto mais importante é que Classe é o tipo de referência, enquanto Estrutura é o tipo de valor. por exemplo,
agora vamos criar instância de ambos.
agora vamos passar essas instâncias para duas funções que modificam o id, descrição, destino etc.
Além disso,
tão,
Agora, se imprimirmos o ID e a descrição do flightA, obteremos
Aqui, podemos ver o ID e a descrição do FlightA alterados porque o parâmetro passado para o método modify realmente aponta para o endereço de memória do objeto flightA (tipo de referência).
Agora, se imprimirmos o ID e a descrição da instância FLightB que obtivermos,
Aqui podemos ver que a instância do FlightB não foi alterada, porque no método modifyFlight2, a instância real do Flight2 é passada em vez de referência (tipo de valor).
fonte
Here we can see that the FlightB instance is not changed
Structs
sãovalue type
eClasses
sãoreference type
Use um
value
tipo quando:Use um
reference
tipo quando:Informações adicionais também podem ser encontradas na documentação da Apple
https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
informação adicional
Os tipos de valores rápidos são mantidos na pilha. Em um processo, cada encadeamento possui seu próprio espaço de pilha, portanto, nenhum outro encadeamento poderá acessar diretamente seu tipo de valor. Portanto, não há condições de corrida, bloqueios, deadlocks ou qualquer complexidade de sincronização de encadeamentos relacionada.
Os tipos de valor não precisam de alocação dinâmica de memória ou contagem de referência, ambas operações caras. Ao mesmo tempo, métodos em tipos de valor são despachados estaticamente. Isso cria uma enorme vantagem em favor dos tipos de valor em termos de desempenho.
Como lembrete, aqui está uma lista de Swift
Tipos de valor:
Tipos de referência:
fonte
Respondendo à pergunta da perspectiva de tipos de valor versus tipos de referência, a partir desta publicação no blog da Apple , pareceria muito simples:
Como mencionado nesse artigo, uma classe sem propriedades graváveis se comportará de forma idêntica a uma struct, com (acrescentarei) uma ressalva: as estruturas são melhores para modelos seguros para threads - um requisito cada vez mais iminente na arquitetura moderna de aplicativos.
fonte
Com as classes, você obtém herança e é passado por referência, as estruturas não têm herança e são passadas por valor.
Há ótimas sessões da WWDC no Swift, essa pergunta específica é respondida em detalhes em um deles. Assista a eles, pois você poderá acelerar muito mais rapidamente do que o Guia de idiomas ou o iBook.
fonte
Eu não diria que as estruturas oferecem menos funcionalidade.
Claro, o eu é imutável, exceto em uma função mutante, mas é isso.
A herança funciona bem desde que você mantenha a boa e velha idéia de que toda classe deve ser abstrata ou final.
Implementar classes abstratas como protocolos e classes finais como estruturas.
O bom das estruturas é que você pode tornar seus campos mutáveis sem criar um estado mutável compartilhado, porque a cópia na gravação cuida disso :)
É por isso que as propriedades / campos no exemplo a seguir são todas mutáveis, o que eu não faria nas classes Java ou C # ou swift .
Exemplo de estrutura de herança com um pouco de uso sujo e direto na parte inferior da função chamada "exemplo":
fonte
Padrão de criação:
Em rápida, Struct é um tipo de valor que é automaticamente clonado. Portanto, obtemos o comportamento necessário para implementar o padrão de protótipo gratuitamente.
Considerando que as classes são o tipo de referência, que não é automaticamente clonado durante a tarefa. Para implementar o padrão de protótipo, as classes devem adotar o
NSCopying
protocolo.A cópia rasa duplica apenas a referência, que aponta para esses objetos, enquanto a cópia profunda duplica a referência do objeto.
A implementação de cópia profunda para cada tipo de referência tornou-se uma tarefa tediosa. Se as classes incluem outro tipo de referência, temos que implementar o padrão de protótipo para cada uma das propriedades de referência. E então temos que copiar todo o gráfico de objetos implementando o
NSCopying
protocolo.Usando structs e enums , tornamos nosso código mais simples, pois não precisamos implementar a lógica de cópia.
fonte
Muitas APIs de cacau exigem subclasses NSObject, o que o força a usar a classe. Mas, além disso, você pode usar os seguintes casos do blog Swift da Apple para decidir se deve usar um tipo de valor struct / enum ou um tipo de referência de classe.
https://developer.apple.com/swift/blog/?id=10
fonte
Um ponto que não chama a atenção nessas respostas é que uma variável que mantém uma classe versus uma estrutura pode demorar um
let
pouco para permitir alterações nas propriedades do objeto, enquanto você não pode fazer isso com uma estrutura.Isso é útil se você não quiser que a variável aponte para outro objeto, mas ainda precise modificá-lo, ou seja, no caso de ter muitas variáveis de instância que deseja atualizar uma após a outra. Se for uma estrutura, você deve permitir que a variável seja redefinida para outro objeto usando-a
var
para fazer isso, pois um tipo de valor constante no Swift permite corretamente uma mutação zero, enquanto os tipos de referência (classes) não se comportam dessa maneira.fonte
Como struct são tipos de valor e você pode criar a memória com muita facilidade que é armazenada na pilha. A estrutura pode ser facilmente acessível e, após o escopo do trabalho, é facilmente desalocada da memória da pilha e pop da parte superior da pilha. Por outro lado, classe é um tipo de referência que armazena no heap e as alterações feitas em um objeto de classe terão impacto em outro objeto, pois são fortemente acoplados e tipo de referência.Todos os membros de uma estrutura são públicos, enquanto todos os membros de uma classe são privados. .
As desvantagens do struct é que ele não pode ser herdado.
fonte
Estrutura e classe são tipos de dados desafiados pelo usuário
Por padrão, a estrutura é pública, enquanto a classe é privada.
Classe implementa o principal do encapsulamento
Objetos de uma classe são criados na memória heap
A classe é usada para reutilização, enquanto a estrutura é usada para agrupar os dados na mesma estrutura
Os membros dos dados da estrutura não podem ser inicializados diretamente, mas podem ser atribuídos por fora da estrutura
Membros de dados da classe podem ser inicializados diretamente pelo parâmetro menos construtor e atribuídos pelo construtor parametrizado
fonte