willset lazy did swift properties didset

did - lazy var swift



Swift: cómo cambiar el valor de una propiedad sin llamar a su función didSet (4)

¿Cómo puede establecer el valor de una propiedad en Swift, sin llamar a su función didSet() fuera de un contexto de inicialización? El código a continuación fue un experimento fallido para lograr esto dentro de la función noside() las clases

class Test { var toggle : Bool = 0 var hoodwink : Int = 0 { didSet(hoodwink) { toggle = !toggle } } // failed attempt to set without a side effect func noside(newValue : Int) { hoodwink = newValue println("hoodwink: /(hoodwink) state: /(toggle)") } func withside(newValue : Int) { self.hoodwink = newValue println("hoodwink: /(hoodwink) state: /(toggle)") } }

Es bastante trivial hacerlo en Objective-C con propiedades auto-sintetizadas:

Con efecto secundario (si está presente en el setter):

self.hoodwink = newValue;

Sin efecto secundario:

_hoodwink = newValue;


El problema que estaba tratando de resolver es que quería obtener una llamada doCommonUpdate cuando un valor cambiaba, o uno si ambos cambiaban al mismo tiempo. Hacer esto creó una recursión porque si cualquiera de los valores cambia, llamaría a didSet cada vez. Ejemplo de configuración, el mío estuvo más involucrado:

class Person { var first: String = "" { didSet { updateValues(first: first, last: last) } } var last: String = "" { didSet { updateValues(first: first, last: last) } } init() {} init(first: String, last: String) { self.first = first self.last = last } // also want to set both at the same time. func updateValues(first: String, last: String) { // self.first = first // causes recursion. // self.last = last doCommonSetup(first: first, last: last) } func doCommonSetup(first: String, last: String) { print(first, last) } } let l = Person() l.first = "Betty" l.last = "Rubble" _ = Person(first: "Wilma", last: "Flintstone") > Betty > Betty Rubble

Nota: Wilma no imprime debido a las líneas comentadas.

Mi solución fue mover todas esas variables a una estructura separada. Este enfoque resuelve el problema y tiene el beneficio secundario de crear otra agrupación que ayuda al significado. Todavía obtenemos el doCommonSetup cuando cualquiera de los valores cambia de forma independiente y cuando cambiamos ambos valores al mismo tiempo.

class Person2 { struct Name { let first: String let last: String } var name: Name init() { name = Name(first: "", last: "") } init(first: String, last: String) { name = Name(first: first, last: last) updateValues(name: name) } var first: String = "" { didSet { name = Name(first: first, last: self.last) updateValues(name: name) } } var last: String = "" { didSet { name = Name(first: self.first, last: last) updateValues(name: name) } } // also want to set both at the same time. func updateValues(name: Name) { self.name = name doCommonSetup(name: name) } func doCommonSetup(name: Name) { print(name.first, name.last) } } let p = Person2() p.first = "Barney" p.last = "Rubble" _ = Person2(first: "Fred", last: "Flintstone") > Barney > Barney Rubble > Fred Flintstone


Lo que hace en Objective-C para "evitar efectos secundarios" es acceder al almacén de respaldo de la propiedad, su variable de instancia, que está prefijada con un guión bajo de forma predeterminada (puede cambiar esto usando la directiva @synthesize ).

Sin embargo, parece que los diseñadores de lenguaje de Swift tuvieron especial cuidado en hacer imposible el acceso a las variables de respaldo de las propiedades: de acuerdo con el libro,

Si tiene experiencia con Objective-C, puede saber que proporciona dos formas de almacenar valores y referencias como parte de una instancia de clase. Además de las propiedades, puede utilizar variables de instancia como un almacén de respaldo para los valores almacenados en una propiedad.

Swift unifica estos conceptos en una sola declaración de propiedad. Una propiedad Swift no tiene una variable de instancia correspondiente, y no se accede directamente al almacén de respaldo de una propiedad . (el énfasis es mío)

Por supuesto, esto se aplica solo al uso de los medios de "lenguaje regular", en lugar de usar la reflexión: podría proporcionar una forma de evitar esta restricción, a costa de la legibilidad.


Si sabe exactamente cuándo desea aplicar efectos secundarios, hágalo explícitamente:

1 Solución:

func noside(newValue : Int) { hoodwink = newValue } func withside(newValue : Int) { self.hoodwink = newValue toggle = !toggle }

2 Solución:

var toggle : Bool = false var hoodwink : Int = 0 var hoodwinkToggle: Int { get { return hoodwink } set(newValue) { hoodwink = newValue toggle = !toggle } }

  1. func setHoodwinkWithToggle (hoodwink: Int) {...}
  2. ....

Creo que estas soluciones serán más claras y legibles, luego usar una variable que en algunas llamadas debería tener efectos secundarios y no en otras.


Un posible truco para esto es proporcionar un setter que omita tu didSet

var dontTriggerObservers:Bool = false var selectedIndexPath:NSIndexPath? { didSet { if(dontTriggerObservers == false){ //blah blah things to do } } } var primitiveSetSelectedIndexPath:NSIndexPath? { didSet(indexPath) { dontTriggerObservers = true selectedIndexPath = indexPath dontTriggerObservers = false } }

Feo pero realizable