El atributo de mutabilidad está marcado en un almacenamiento (constante o variable), no en un tipo. Puede pensar que la estructura tiene dos modos: mutable e inmutable . Si asigna un valor de estructura a un almacenamiento inmutable (lo llamamos let
o constante en Swift), el valor se convierte en modo inmutable y no puede cambiar ningún estado en el valor. (incluida la llamada a cualquier método mutante)
Si el valor se asigna a un almacenamiento mutable (lo llamamos var
o variable en Swift), puede modificar su estado y se permite la llamada al método mutante.
Además, las clases no tienen este modo inmutable / mutable. En mi opinión, esto se debe a que las clases se utilizan generalmente para representar entidades referenciables . Y la entidad a la que se puede hacer referencia suele ser mutable porque es muy difícil hacer y administrar gráficos de referencia de entidades de manera inmutable con un rendimiento adecuado. Es posible que agreguen esta función más adelante, pero al menos no ahora.
Para los programadores de Objective-C, los conceptos mutables / inmutables son muy familiares. En Objective-C teníamos dos clases separadas para cada concepto, pero en Swift, puedes hacer esto con una estructura. Medio trabajo.
Para los programadores de C / C ++, este también es un concepto muy familiar. Esto es exactamente lo que const
hacen las palabras clave en C / C ++.
Además, el valor inmutable se puede optimizar muy bien. En teoría, el compilador Swift (o LLVM) puede realizar una copia-elisión en los valores pasados let
, como en C ++. Si usa la estructura inmutable sabiamente, superará a las clases refcontadas.
Actualizar
Como @Joseph afirmó que esto no explica por qué , estoy agregando un poco más.
Las estructuras tienen dos tipos de métodos. métodos simples y mutantes . El método simple implica inmutable (o no mutante) . Esta separación sólo existe para apoyar inmutables semántica. Un objeto en modo inmutable no debería cambiar su estado en absoluto.
A continuación, los métodos inmutables deben garantizar esta inmutabilidad semántica . Lo que significa que no debería cambiar ningún valor interno. Así que el compilador no permite ningún cambio de estado de sí mismo en un método inmutable. Por el contrario, los métodos de mutación son libres de modificar estados.
Y luego, es posible que tenga una pregunta de por qué inmutable es el defecto? Esto se debe a que es muy difícil predecir el estado futuro de los valores mutantes, y eso generalmente se convierte en la principal fuente de dolores de cabeza y errores. Mucha gente estuvo de acuerdo en que la solución es evitar cosas mutables, y luego inmutable por defecto estuvo en la parte superior de la lista de deseos durante décadas en los lenguajes de la familia C / C ++ y sus derivaciones.
Ver estilo puramente funcional para más detalles. De todos modos, todavía necesitamos cosas mutables porque las cosas inmutables tienen algunas debilidades, y discutir sobre ellas parece estar fuera de tema.
Espero que esto ayude.
const
al método si desea que tenga un 'const this' y se permita en instancias constantes (los métodos normales no se pueden usar si la instancia es constante). Swift hace lo mismo con lo contrario por defecto: un método tiene un 'const this' por defecto y usted lo marca de otra manera si tiene que estar prohibido para instancias constantes.Una estructura es una agregación de campos; si una instancia de estructura particular es mutable, sus campos serán mutables; si una instancia es inmutable, sus campos serán inmutables. Por tanto, se debe preparar un tipo de estructura para la posibilidad de que los campos de cualquier instancia particular puedan ser mutables o inmutables.
Para que un método de estructura mute los campos de la estructura subyacente, esos campos deben ser mutables. Si un método que muta campos de la estructura subyacente se invoca sobre una estructura inmutable, intentaría mutar campos inmutables. Dado que nada bueno podría salir de eso, tal invocación debe prohibirse.
Para lograr eso, Swift divide los métodos de estructura en dos categorías: aquellos que modifican la estructura subyacente y, por lo tanto, solo pueden invocarse en instancias de estructura mutable, y aquellos que no modifican la estructura subyacente y, por lo tanto, deberían ser invocables tanto en instancias mutables como inmutables. . Este último uso es probablemente el más frecuente y, por tanto, el predeterminado.
En comparación, .NET actualmente (¡todavía!) No ofrece ningún medio para distinguir los métodos de estructura que modifican la estructura de aquellos que no lo hacen. En cambio, invocar un método de estructura en una instancia de estructura inmutable hará que el compilador haga una copia mutable de la instancia de estructura, deje que el método haga lo que quiera con él y descarte la copia cuando el método esté listo. Esto tiene el efecto de obligar al compilador a perder tiempo copiando la estructura, ya sea que el método la modifique o no, aunque agregar la operación de copia casi nunca convertirá lo que sería un código semánticamente incorrecto en un código semánticamente correcto; simplemente hará que el código que es semánticamente incorrecto de una manera (modificando un valor "inmutable") sea incorrecto de otra manera (permitiendo que el código piense que está modificando una estructura, pero descartando los cambios intentados). Permitir que los métodos de estructura indiquen si modificarán la estructura subyacente puede eliminar la necesidad de una operación de copia inútil y también asegura que se marcará el intento de uso erróneo.
fuente
Precaución: términos legos por delante.
Esta explicación no es rigurosamente correcta en el nivel de código más esencial. Sin embargo, ha sido revisado por un tipo que realmente trabaja en Swift y dijo que es lo suficientemente bueno como explicación básica.
Así que quiero intentar responder de forma simple y directa a la pregunta de "por qué".
Para ser precisos: ¿por qué tenemos que marcar las funciones de estructura
mutating
cuando podemos cambiar los parámetros de estructura sin modificar las palabras clave?Entonces, en general, tiene mucho que ver con la filosofía que mantiene a Swift veloz.
Podría pensar en ello como el problema de administrar direcciones físicas reales. Cuando cambie su dirección, si hay muchas personas que tienen la actual, debe notificarles a todas que se ha mudado. Pero si nadie tiene su dirección actual, simplemente puede mudarse a donde quiera, y nadie necesita saberlo.
En esta situación, Swift es como la oficina de correos. Si muchas personas con muchos contactos se mueven mucho, hay una sobrecarga muy alta. Tiene que pagar una gran cantidad de personas para manejar todas esas notificaciones, y el proceso requiere mucho tiempo y esfuerzo. Es por eso que el estado ideal de Swift es que todos en su ciudad tengan la menor cantidad de contactos posible. Entonces no necesita un gran personal para manejar los cambios de dirección y puede hacer todo lo demás más rápido y mejor.
Esta es también la razón por la que la gente de Swift está entusiasmada con los tipos de valor frente a los tipos de referencia. Por naturaleza, los tipos de referencia acumulan "contactos" por todas partes, y los tipos de valor no suelen necesitar más de un par. Los tipos de valor son "Swift" -er.
Así que de vuelta a la imagen pequeña:
structs
. Las estructuras son un gran problema en Swift porque pueden hacer la mayoría de las cosas que pueden hacer los objetos, pero son tipos de valor.Continuemos con la analogía de la dirección física imaginando un
misterStruct
que vive ensomeObjectVille
. La analogía se torna un poco complicada aquí, pero creo que todavía es útil.Entonces, para modelar el cambio de una variable en a
struct
, digamos quemisterStruct
tiene cabello verde y recibe una orden para cambiar a cabello azul. La analogía se torna torpe, como dije, pero más o menos lo que sucede es que en lugar de cambiarsemisterStruct
el cabello, la persona mayor se muda y entra una nueva persona con cabello azul , y esa nueva persona comienza a llamarse a sí mismamisterStruct
. Nadie necesita recibir una notificación de cambio de dirección, pero si alguien mira esa dirección, verá a un tipo con el pelo azul.Ahora modelemos lo que sucede cuando llamas a una función en a
struct
. En este caso, es comomisterStruct
recibir un pedido comochangeYourHairBlue()
. Entonces, la oficina de correos da la instrucción demisterStruct
"ir a cambiarte el cabello a azul y decirme cuando hayas terminado".Si sigue la misma rutina que antes, si está haciendo lo que hizo cuando se cambió la variable directamente, lo
misterStruct
que hará es mudarse de su propia casa y llamar a una nueva persona con cabello azul. Pero ese es el problema.La orden era "ve a cambiarte el pelo a azul y dime cuando termines", pero es el chico verde quien recibió esa orden. Después de que el chico azul se mude, aún debe enviarse una notificación de "trabajo completado". Pero el chico azul no sabe nada al respecto.
[Para realmente pensar en esta analogía algo horrible, lo que técnicamente le sucedió al chico de cabello verde fue que después de mudarse, inmediatamente se suicidó. ¡Así que tampoco puede notificar a nadie que la tarea está completa ! ]
Para evitar este problema, en casos como éste solamente , Swift tiene que ir directamente a la casa en esa dirección y en realidad cambiar el pelo de la fábrica actual . Ese es un proceso completamente diferente a simplemente enviar a un chico nuevo.
¡Y es por eso que Swift quiere que usemos la
mutating
palabra clave!El resultado final se ve igual para cualquier cosa que tenga que referirse a la estructura: el habitante de la casa ahora tiene el pelo azul. Pero los procesos para lograrlo son en realidad completamente diferentes. Parece que está haciendo lo mismo, pero está haciendo algo muy diferente. Está haciendo algo que las estructuras Swift en general nunca hacen.
Entonces, para darle un poco de ayuda al compilador pobre, y no hacer que tenga que averiguar si una función muta
struct
o no, por sí sola, para cada función de estructura, se nos pide que tengamos lástima y usemos lamutating
palabra clave.En esencia, para ayudar a Swift a mantenerse veloz, todos debemos hacer nuestra parte. :)
EDITAR:
Hola amigo / dudette que me votó negativamente, simplemente reescribí completamente mi respuesta. Si le sienta mejor, ¿eliminará el voto negativo?
fuente
Las estructuras rápidas se pueden instanciar como constantes (vía
let
) o variables (víavar
)Considere la
Array
estructura de Swift (sí, es una estructura).¿Por qué el anexo no funcionó con los nombres de los planetas? Porque agregar está marcado con la
mutating
palabra clave. Y comoplanetNames
se declaró usinglet
, todos los métodos así marcados están fuera de los límites.En su ejemplo, el compilador puede decirle que está modificando la estructura asignando una o más de sus propiedades fuera de un
init
. Si cambia un poco su código, lo veráx
yy
no siempre es accesible fuera de la estructura. Observe ellet
en la primera línea.fuente
Considere una analogía con C ++. Un método de estructura en Swift siendo
mutating
/ not-mutating
es análogo a un método en C ++ que no esconst
/const
. Un método marcadoconst
en C ++ de manera similar no puede mutar la estructura.En C ++, también puede "cambiar una var desde fuera de la estructura", pero solo si tiene una
const
variable no estructura. Si tiene unaconst
variable de estructura, no puede asignar a una var, y tampoco puede llamar a unconst
método que no sea . De manera similar, en Swift, puede cambiar una propiedad de una estructura solo si la variable de estructura no es una constante. Si tiene una constante de estructura, no puede asignar a una propiedad y tampoco puede llamar a unmutating
método.fuente
Me pregunté lo mismo cuando comencé a aprender Swift, y cada una de estas respuestas, aunque quizás agregue algunas ideas, son un poco prolijas y confusas por derecho propio. Creo que la respuesta a tu pregunta es bastante simple ...
El método de mutación definido dentro de su estructura quiere permiso para modificar todas y cada una de las instancias de sí mismo que se crearán en el futuro. ¿Qué pasa si una de esas instancias se asigna a una constante inmutable con
let
? UH oh. Para protegerte de ti mismo (y para que el editor y el compilador sepan lo que estás tratando de hacer), estás obligado a ser explícito cuando quieras darle a un método de instancia este tipo de poder.Por el contrario, la configuración de una propiedad desde fuera de su estructura está operando en una instancia conocida de esa estructura. Si está asignado a una constante, Xcode te lo hará saber en el momento en que hayas escrito la llamada al método.
Esta es una de las cosas que me encanta de Swift a medida que empiezo a usarlo más: ser alertado de errores a medida que los escribo. ¡Seguro que es mejor que la solución de problemas de errores oscuros de JavaScript!
fuente
SWIFT: uso de función de mutación en estructuras
Los programadores de Swift desarrollaron Structs de tal manera que sus propiedades no se pueden modificar dentro de los métodos de Struct. Por ejemplo, verifique el código que se proporciona a continuación
Al ejecutar el código anterior obtenemos un error porque estamos tratando de asignar un nuevo valor a la población de propiedades de Struct City. Por defecto, las propiedades de Structs no se pueden modificar dentro de sus propios métodos. Así es como lo han construido los desarrolladores de Apple, de modo que los Structs tendrán una naturaleza estática por defecto.
¿Como lo resolvemos? Cual es la alternativa?
Palabra clave mutante:
Declarar la función como mutante dentro de Struct nos permite alterar propiedades en Structs. Línea No: 5, del código anterior cambia a esto,
Ahora podemos asignar el valor de la nueva población a la población de la propiedad dentro del alcance del método.
Nota :
Si usamos let en lugar de var para los objetos Struct, entonces no podemos mutar el valor de ninguna propiedad. Además, esta es la razón por la que obtenemos un error cuando intentamos invocar la función mutante usando la instancia let. Por lo tanto, es mejor usar var siempre que cambie el valor de una propiedad.
Me encantaría escuchar sus comentarios y pensamientos… ..
fuente