swift class structure constants

swift - ¿Por qué las restricciones constantes de la propiedad de una instancia de estructura pero no la instancia de clase?



class structure (2)

Cuando trato de cambiar la propiedad de ID de la instancia byValueObj , recibí un error que me dijo que no puedo asignar la propiedad de una constante, aunque la propiedad es una variable. Sin embargo, puedo hacerlo en una instancia de clase. Como que sé que tal vez tenga algo que ver con el valor por y el mecanismo de referencia. Pero no tengo una comprensión muy clara y correcta de ello. ¿Alguien me lo puede explicar? Gracias.

struct CreatorValue{ var ID = 2201 } class CreatorRefer{ var ID = 2203 } let byValueObj = CreatorValue() let byReferObj = CreatorRefer() byValueObj.ID = 201 //Error: cannot assign to property: ''byValueObj'' is a ''let'' constant byReferObj.ID = 203 //works fine here


Las estructuras en Swift son tipos de valores y, semánticamente hablando, los valores (es decir, ''instancias'' de tipos de valores) son inmutables.

Una mutación de un tipo de valor, ya sea cambiando directamente el valor de una propiedad, o mediante el uso de un método de mutating , es equivalente a simplemente asignar un valor completamente nuevo a la variable que lo contiene (más cualquier efecto secundario que la mutación desencadene). Por lo tanto, la variable que lo sostiene debe ser una var . Y esta semántica se muestra muy bien por el comportamiento de los observadores de propiedades en torno a los tipos de valores, como señala iGodric .

Entonces, lo que esto significa es que puedes pensar en esto:

struct Foo { var bar = 23 var baz = 59 } // ... let foo = Foo() foo.bar = 7 // illegal

como haciendo esto:

let foo = Foo() var fooCopy = foo // temporary mutable copy of foo. fooCopy.bar = 7 // mutate one or more of the of the properties foo = fooCopy // re-assign back to the original (illegal as foo is declared as // a let constant)

Y como puede ver claramente, este código es ilegal. No puede asignar fooCopy nuevo a foo , ya que es una constante let . Por lo tanto, no puede cambiar la propiedad de un tipo de valor que se declara como let y, por lo tanto, necesitaría convertirlo en var .

(Vale la pena señalar que el compilador en realidad no pasa por esta palabrería; puede mutar las propiedades de las estructuras directamente, lo que se puede ver al observar el SIL generado . Sin embargo, esto no cambia la semántica de los tipos de valor).

La razón por la que puede cambiar una propiedad mutable de una instancia de clase let , se debe al hecho de que las clases son tipos de referencia. Por lo tanto, ser una constante constante solo asegura que la referencia permanezca igual. La mutación de sus propiedades no afecta de ninguna manera su referencia a ellas: todavía se está refiriendo a la misma ubicación en la memoria.

Puede pensar en un tipo de referencia como una señal, por lo tanto, codifique así:

class Foo { var bar = 23 var baz = 59 } // ... let referenceToFoo = Foo()

Puedes pensar en la representación de la memoria de esta manera:

| referenceToFoo | ---> | Underlying Foo instance | | (a reference to 0x2A) | |<----------------------->| |0x2A |0x32 |0x3A | bar: Int | baz : Int | | 23 | 59 |

Y cuando mutas una propiedad:

referenceToFoo.bar = 203

La referencia ( referenceToFoo ) en sí misma no se ve afectada: todavía está apuntando a la misma ubicación en la memoria. Es la propiedad de la instancia subyacente la que ha cambiado (lo que significa que la instancia subyacente fue mutada):

| referenceToFoo | ---> | Underlying Foo instance | | (a reference to 0x2A) | |<----------------------->| |0x2A |0x32 |0x3A | bar: Int | baz : Int | | 203 | 59 |

Solo cuando intente asignar una nueva referencia a referenceToFoo , el compilador le dará un error, ya que está intentando mutar la referencia en sí:

// attempt to assign a new reference to a new Foo instance to referenceToFoo. // will produce a compiler error, as referenceToFoo is declared as a let constant. referenceToFoo = Foo()

Por lo tanto, necesitaría hacer referenceToFoo a var para que esta asignación sea legal.


struct es un tipo de valor. Si los edita, está llamando al configurador predeterminado para la propiedad que no es más que un método de mutación, que no es más que un método estático de la estructura que tiene self como primer argumento como inout que devuelve el método (Swift por ahora tiene curry sintaxis para llamadas a métodos no aplicados, pero la cambiará a una aplanada ). Solo como una nota al margen: cuando el método no está mutando, no será inout .

Debido a que inout está funcionando copiando - copiando , se llama a didSet , incluso si nada ha cambiado.

"Si pasa una propiedad que tiene observadores a una función como parámetro de entrada, los observadores willSet y didSet siempre se llaman". Extracto de: Apple Inc. "El lenguaje de programación Swift (Swift 3)". IBooks. https://itun.es/de/jEUH0.l

Código que se copiará una estructura var cuando se mute una propiedad:

struct Size { var width: Int var height: Int } struct Rectangle { var size: Size } var screenResolution = Rectangle.init(size: Size.init(width: 640, height: 480)) { didSet { print("Did set the var ''screenResolution'' to a new value! New value is /(screenResolution)") } } screenResolution.size.width = 800

Llama al didSet aunque solo didSet un Int en la propiedad de la estructura.

Si fuera una copia nueva completa, esperaría que la estructura mutada fuera una copia nueva con una nueva asignación de memoria. Pero esto no es lo que sucede en el ejemplo, vea el código a continuación.

// Value of a pointer is the address to the thing it points to internal func pointerValue(of pointer: UnsafeRawPointer) -> Int { return unsafeBitCast(pointer, to: Int.self) } internal struct MyStruct { internal var a: Int } internal var struct1: MyStruct = MyStruct.init(a: 1) pointerValue(of: &struct1) // output: 4405951104 struct1.a = 2 pointerValue(of: &struct1) // output: 4405951104

Entonces la estructura no se copia. Pero porque es inout :

"Escriba su código utilizando el modelo proporcionado por la copia de copia de salida, sin depender de la optimización de llamada por referencia, para que se comporte correctamente con o sin la optimización". Extracto de: Apple Inc. "El lenguaje de programación Swift (Swift 3) ". IBooks. https://itun.es/de/jEUH0.l

Ejemplo de código con solo inout :

struct MyType { var x: Int mutating func m1(y: Int) -> Int { x += 1 return x + y } } let mytypem1: (inout MyType) -> (Int) -> Int = MyType.m1 var myType = MyType.init(x: 1) // has to be "var" mytypem1(&myType)(2) // returns 3