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
}
}
- func setHoodwinkWithToggle (hoodwink: Int) {...}
- ....
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