Jugando con Swift, viniendo de un fondo de Java, ¿por qué querrías elegir una Estructura en lugar de una Clase? Parece que son lo mismo, con un Struct que ofrece menos funcionalidad. ¿Por qué elegirlo entonces?
swift
class
struct
design-principles
bluedevil2k
fuente
fuente
Respuestas:
De acuerdo con la muy popular charla WWDC 2015, Protocolo orientado a la programación en Swift ( video , transcripción ), Swift proporciona una serie de características que hacen que las estructuras sean mejores que las clases en muchas circunstancias.
Las estructuras son preferibles si son relativamente pequeñas y copiables porque la copia es mucho más segura que tener múltiples referencias a la misma instancia que sucede con las clases. Esto es especialmente importante cuando se pasa una variable a muchas clases y / o en un entorno multiproceso. Si siempre puede enviar una copia de su variable a otros lugares, nunca tendrá que preocuparse de que ese otro lugar cambie el valor de su variable debajo de usted.
Con Structs, hay mucho menos necesidad de preocuparse por fugas de memoria o múltiples subprocesos para acceder / modificar una sola instancia de una variable. (Para los más técnicos, la excepción es cuando se captura una estructura dentro de un cierre porque en realidad se está capturando una referencia a la instancia a menos que se marque explícitamente para ser copiada).
Las clases también pueden hincharse porque una clase solo puede heredar de una sola superclase. Eso nos anima a crear grandes superclases que abarcan muchas habilidades diferentes que solo están poco relacionadas. El uso de protocolos, especialmente con extensiones de protocolo donde puede proporcionar implementaciones a protocolos, le permite eliminar la necesidad de clases para lograr este tipo de comportamiento.
La charla presenta estos escenarios donde se prefieren las clases:
Implica que las estructuras deberían ser las predeterminadas y las clases deberían ser una alternativa.
Por otro lado, la documentación del lenguaje de programación Swift es algo contradictoria:
Aquí se afirma que deberíamos usar clases por defecto y usar estructuras solo en circunstancias específicas. En última instancia, debe comprender la implicación del mundo real de los tipos de valor frente a los tipos de referencia y luego puede tomar una decisión informada sobre cuándo usar estructuras o clases. Además, tenga en cuenta que estos conceptos siempre están evolucionando y que la documentación de The Swift Programming Language fue escrita antes de la charla de programación orientada al protocolo.
fuente
In practice, this means that most custom data constructs should be classes, not structures.
¿Puede explicarme cómo, después de leer eso, obtiene que la mayoría de los conjuntos de datos deben ser estructuras y no clases? Dieron un conjunto específico de reglas cuando algo debería ser una estructura y prácticamente dijeron "todos los demás escenarios una clase es mejor".Dado que las instancias de estructura se asignan en la pila y las instancias de clase se asignan en el montón, las estructuras a veces pueden ser drásticamente más rápidas.
Sin embargo, siempre debe medirlo usted mismo y decidir en función de su caso de uso único.
Considere el siguiente ejemplo, que muestra 2 estrategias para ajustar
Int
el tipo de datos usandostruct
yclass
. Estoy usando 10 valores repetidos para reflejar mejor el mundo real, donde tienes múltiples campos.El rendimiento se mide usando
El código se puede encontrar en https://github.com/knguyen2708/StructVsClassPerformance
ACTUALIZACIÓN (27 de marzo de 2018) :
A partir de Swift 4.0, Xcode 9.2, ejecutando Release build en iPhone 6S, iOS 11.2.6, la configuración del compilador Swift es
-O -whole-module-optimization
:class
la versión tardó 2.06 segundosstruct
la versión tomó 4.17e-08 segundos (50,000,000 veces más rápido)(Ya no promedio varias ejecuciones, ya que las variaciones son muy pequeñas, menos del 5%)
Nota : la diferencia es mucho menos dramática sin la optimización completa del módulo. Me alegraría si alguien puede señalar lo que realmente hace la bandera.
ACTUALIZACIÓN (7 de mayo de 2016) :
A partir de Swift 2.2.1, Xcode 7.3, que ejecuta Release build en iPhone 6S, iOS 9.3.1, con un promedio de más de 5 ejecuciones, la configuración del compilador Swift es
-O -whole-module-optimization
:class
la versión tomó 2.159942142sstruct
la versión tomó 5.83E-08s (37,000,000 veces más rápido)Nota : como alguien mencionó que en escenarios del mundo real, es probable que haya más de 1 campo en una estructura, he agregado pruebas para estructuras / clases con 10 campos en lugar de 1. Sorprendentemente, los resultados no varían mucho.
RESULTADOS ORIGINALES (1 de junio de 2014):
(Ejecutado en struct / class con 1 campo, no 10)
A partir de Swift 1.2, Xcode 6.3.2, ejecutando Release build en iPhone 5S, iOS 8.3, promedió más de 5 ejecuciones
class
la versión tomó 9.788332333sstruct
la versión tardó 0.010532942s (900 veces más rápido)ANTIGUOS RESULTADOS (de tiempo desconocido)
(Ejecutado en struct / class con 1 campo, no 10)
Con la versión de lanzamiento en mi MacBook Pro:
class
versión tomó 1.10082 segundosstruct
versión tomó 0.02324 segundos (50 veces más rápido)fuente
Similitudes entre estructuras y clases.
Creé gist para esto con ejemplos simples. https://github.com/objc-swift/swift-classes-vs-structures
Y diferencias
1. Herencia.
Las estructuras no se pueden heredar rápidamente. Si tu quieres
Ve a una clase.
2. Pase por
Las estructuras rápidas pasan por valor y las instancias de clase pasan por referencia.
Diferencias contextuales
Estructura constante y variables
Ejemplo (utilizado en WWDC 2014)
Define una estructura llamada Punto.
Ahora si trato de cambiar la x. Es una expresión válida.
Pero si definía un punto como constante.
En este caso, todo el punto es constante inmutable.
Si usé una clase Point en su lugar, esta es una expresión válida. Porque en una clase, la constante inmutable es la referencia a la clase en sí, no a sus variables de instancia (a menos que esas variables se definan como constantes)
fuente
Aquí hay otras razones para considerar:
Las estructuras obtienen un inicializador automático que no tiene que mantener en el código en absoluto.
Para obtener esto en una clase, tendría que agregar el inicializador y mantener el inicializador ...
Los tipos básicos de colección como
Array
son structs. Cuanto más los use en su propio código, más se acostumbrará a pasar por valor en lugar de referencia. Por ejemplo:Aparentemente, la inmutabilidad frente a la mutabilidad es un tema enorme, pero mucha gente inteligente piensa que la inmutabilidad, estructuras en este caso, es preferible. Objetos mutables vs inmutables
fuente
internal
alcance.mutating
para que sea explícito sobre qué funciones cambian su estado. Pero su naturaleza como tipos de valor es lo importante. Si declaras una estructura conlet
no puedes invocar ninguna función mutante en ella. El video de WWDC 15 sobre Mejor programación a través de tipos de valor es un excelente recurso para esto.Suponiendo que sepamos que Struct es un tipo de valor y Class es un tipo de referencia .
Si no sabe qué son un tipo de valor y un tipo de referencia, vea ¿Cuál es la diferencia entre pasar por referencia frente a pasar por valor?
Según la publicación de mikeash :
Yo personalmente no llamo así a mis clases. Por lo general, llamo a mi UserManager en lugar de UserController, pero la idea es la misma
Además, no use la clase cuando tenga que anular todas y cada una de las funciones de una función, es decir, que no tengan ninguna funcionalidad compartida .
Entonces, en lugar de tener varias subclases de una clase. Use varias estructuras que se ajusten a un protocolo.
Otro caso razonable para las estructuras es cuando desea hacer un delta / diff de su modelo antiguo y nuevo. Con los tipos de referencias no puede hacer eso de forma inmediata. Con los tipos de valor, las mutaciones no se comparten.
fuente
Algunas ventajas:
fuente
La estructura es mucho más rápida que la clase. Además, si necesita herencia, debe usar Class. El punto más importante es que Class es un tipo de referencia, mientras que Structure es un tipo de valor. por ejemplo,
ahora vamos a crear una instancia de ambos.
ahora pasemos esta instancia a dos funciones que modifican la identificación, la descripción, el destino, etc.
además,
entonces,
ahora si imprimimos la identificación y la descripción del vuelo A, obtenemos
Aquí, podemos ver que la identificación y la descripción de FlightA cambian porque el parámetro pasado al método de modificación en realidad apunta a la dirección de memoria del objeto flightA (tipo de referencia).
ahora si imprimimos el id y la descripción de la instancia de FLightB que obtenemos,
Aquí podemos ver que la instancia de FlightB no cambia porque en el método modifyFlight2, la instancia real de Flight2 es pasa en lugar de referencia (tipo de valor).
fuente
Here we can see that the FlightB instance is not changed
Structs
sonvalue type
yClasses
sonreference type
Use un
value
tipo cuando:Use un
reference
tipo cuando:También se puede encontrar más información en la documentación de Apple
https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
Información Adicional
Los tipos de valores rápidos se mantienen en la pila. En un proceso, cada subproceso tiene su propio espacio de pila, por lo que ningún otro subproceso podrá acceder directamente a su tipo de valor. Por lo tanto, no hay condiciones de carrera, bloqueos, puntos muertos o cualquier complejidad relacionada de sincronización de subprocesos.
Los tipos de valor no necesitan asignación de memoria dinámica o recuento de referencias, las cuales son operaciones costosas. Al mismo tiempo, los métodos sobre los tipos de valor se envían estáticamente. Estos crean una gran ventaja a favor de los tipos de valor en términos de rendimiento.
Como recordatorio aquí hay una lista de Swift
Tipos de valor:
Tipos de referencia:
fuente
Respondiendo la pregunta desde la perspectiva de los tipos de valor frente a los tipos de referencia, desde esta publicación del blog de Apple parecería muy simple:
Como se mencionó en ese artículo, una clase sin propiedades de escritura se comportará de manera idéntica con una estructura, con (agregaré) una advertencia: las estructuras son las mejores para los modelos seguros para subprocesos , un requisito cada vez más inminente en la arquitectura moderna de aplicaciones.
fuente
Con las clases obtienes herencia y se pasan por referencia, las estructuras no tienen herencia y se pasan por valor.
Hay excelentes sesiones de WWDC sobre Swift, esta pregunta específica se responde con gran detalle en una de ellas. Asegúrate de verlos, ya que te permitirá acelerar mucho más rápido que la Guía de idiomas o el iBook.
fuente
No diría que las estructuras ofrecen menos funcionalidad.
Claro, self es inmutable, excepto en una función mutante, pero eso es todo.
La herencia funciona bien siempre y cuando mantengas la vieja idea de que cada clase debe ser abstracta o final.
Implemente clases abstractas como protocolos y clases finales como estructuras.
Lo bueno de las estructuras es que puede hacer que sus campos sean mutables sin crear un estado mutable compartido porque la copia en escritura se encarga de eso :)
Es por eso que las propiedades / campos en el siguiente ejemplo son todos mutables, lo que no haría en Java o C # o clases rápidas .
Ejemplo de estructura de herencia con un poco de uso sucio y directo en la parte inferior de la función llamada "ejemplo":
fuente
Patrón creacional:
En swift, Struct es un tipo de valor que se clona automáticamente. Por lo tanto, obtenemos el comportamiento requerido para implementar el patrón prototipo de forma gratuita.
Mientras que las clases son el tipo de referencia, que no se clona automáticamente durante la asignación. Para implementar el patrón prototipo, las clases deben adoptar el
NSCopying
protocolo.La copia superficial duplica solo la referencia, que apunta a esos objetos, mientras que la copia profunda duplica la referencia del objeto.
Implementar una copia profunda para cada tipo de referencia se ha convertido en una tarea tediosa. Si las clases incluyen otro tipo de referencia, tenemos que implementar un patrón prototipo para cada una de las propiedades de referencia. Y luego tenemos que copiar todo el gráfico del objeto implementando el
NSCopying
protocolo.Al usar estructuras y enumeraciones , simplificamos nuestro código ya que no tenemos que implementar la lógica de copia.
fuente
Muchas API de Cocoa requieren subclases de NSObject, lo que te obliga a usar la clase. Pero aparte de eso, puede usar los siguientes casos del blog Swift de Apple para decidir si usar un tipo de valor de estructura / enumeración o un tipo de referencia de clase.
https://developer.apple.com/swift/blog/?id=10
fuente
Un punto que no llama la atención en estas respuestas es que una variable que contiene una clase frente a una estructura puede tardar un
let
tiempo y permitir cambios en las propiedades del objeto, mientras que no puede hacerlo con una estructura.Esto es útil si no desea que la variable apunte a otro objeto, pero todavía necesita modificar el objeto, es decir, en el caso de tener muchas variables de instancia que desea actualizar una tras otra. Si es una estructura, debe permitir que la variable se restablezca a otro objeto por completo
var
para hacerlo, ya que un tipo de valor constante en Swift permite correctamente la mutación cero, mientras que los tipos de referencia (clases) no se comportan de esta manera.fuente
Como struct son tipos de valor y puede crear la memoria muy fácilmente que se almacena en la pila. Se puede acceder fácilmente a la estructura y, después del alcance del trabajo, se desasigna fácilmente de la memoria de la pila a través del pop desde la parte superior de la pila. Por otro lado, la clase es un tipo de referencia que se almacena en el montón y los cambios realizados en un objeto de clase afectarán a otro objeto, ya que están estrechamente acoplados y son de tipo de referencia. Todos los miembros de una estructura son públicos, mientras que todos los miembros de una clase son privados. .
Las desventajas de la estructura es que no se puede heredar.
fuente
Estructura y clase son tipos de datos desafiados por el usuario
Por defecto, la estructura es pública mientras que la clase es privada
La clase implementa el principio de encapsulación
Los objetos de una clase se crean en la memoria del montón
La clase se usa para la reutilización, mientras que la estructura se usa para agrupar los datos en la misma estructura
Los miembros de datos de estructura no pueden inicializarse directamente, pero pueden ser asignados por el exterior de la estructura
Los miembros de datos de clase pueden ser inicializados directamente por el constructor sin parámetros y asignados por el constructor parametrizado
fuente