Programación rápida: getter / setter en propiedad almacenada

102

¿Cómo sobrescribo el setter de la propiedad almacenada en Swift?

En Obj-C, puedo sobrescribir su setter, pero Swift no parece estar contento con el uso de getter / setters para la propiedad almacenada.

Digamos que tengo una Cardclase con una propiedad llamada rank. No quiero que el cliente le dé ningún valor no válido, por lo tanto, en el objetivo-C, puedo sobrescribir setRankpara que realice una verificación adicional. Pero willSeten Swift no parece ayudar porque newValuees constante y no tiene sentido asignar rankporque se llamará al setter en un bucle.

bohanl
fuente
¿Ha encontrado una forma de hacer esto? Yo mismo necesito este tipo de funcionalidad ...
Mihai Fratu
Lo encontré. Mira mi respuesta ...
Mihai Fratu
¿Qué pasa con didGet o analógico?
fnc12

Respuestas:

107

Okay. Al leer la documentación de Apples en Swift, encontré esto :

Si asigna un valor a una propiedad dentro de su propio observador didSet, el nuevo valor que asigne reemplazará al que acaba de establecer.

Entonces todo lo que tienes que hacer es esto:

var rank: Int = 0 {
    didSet {
        // Say 1000 is not good for you and 999 is the maximum you want to be stored there
        if rank >= 1000  {
            rank = 999
        }
    }
}
Mihai Fratu
fuente
¿Qué pasa con didGet o analógico?
fnc12
No estoy seguro de entender tu pregunta. ¿Puede ser más específico, por favor?
Mihai Fratu
Necesito invocar algún código antes de obtener. Pude realizar esto en obj-c, pero no veo cómo hacerlo en Swift. Lo único que veo es usar dos propiedades: una es pública y la otra es privada, pública invoca mi código y devuelve el valor de la propiedad privada. Por eso pregunté sobre didGet
fnc12
Solo puede tener un captador para una propiedad calculada. Por ejemplovar rankTimesTwo: Int { get { return rank * 2 } }
Mihai Fratu
2
¡Funciona de maravilla! Tenga en cuenta que no se llamará al configurar la propiedad en init ()
Christoph
35

No puede anular get/ setpara una propiedad almacenada, pero puede usar observadores de propiedad willSet/ didSet:

var totalSteps: Int = 0 {
    willSet(newTotalSteps) {
        println("About to set totalSteps to \(newTotalSteps)")
    }
    didSet {
        if totalSteps > oldValue  {
            println("Added \(totalSteps - oldValue) steps")
        }
    }
}

Los nombres de los parámetros predeterminados son newValuepara willSety oldValuepara didSet, o puede nombrarlos usted mismo como en willSet(newTotalSteps).

Joseph Mark
fuente
Esto funciona. Pero no resuelve mi problema, tal vez no fui lo suficientemente claro en mi pregunta original.
bohanl
Digamos que tengo una Cardclase con una propiedad llamada rank. No quiero que el cliente le dé ningún valor, por lo tanto, en el objetivo-C, puedo sobrescribir setRankpara que realice una verificación adicional. Pero willSeten Swift no parece ayudar porque newValuees constante y no tiene sentido asignar rankporque se llamará al setter en un bucle.
bohanl
2
No estoy completamente seguro de lo que quiere decir, pero ¿no podría usar didSetpara verificar rankdespués de que se configura y, si falla la validación, restablecerlo a otra cosa, por ejemplo oldValue?
Joseph Mark
9

get y set son para propiedades calculadas (no tienen ningún almacén de respaldo). (En mi opinión, la palabra clave 'var' es confusa aquí)

  • willSet y didSet se llaman para una variable de instancia (use didSet para anular cualquier cambio)
  • set y get son puramente para propiedades calculadas
usuario3675131
fuente
9

Si no desea utilizar didSet, que tiene el problema de que el valor de la propiedad es temporalmente incorrecto, debe envolverlo con una propiedad calculada.

private var _foo:Int = 0
var foo:Int {
    get {
        return _foo
    }
    set {
        if(newValue > 999) {
            _foo = 999
        } else {
            _foo = newValue
        }
    }
}

O:

private var _foo:Int = 0
var foo:Int {
    get {
        return _foo
    }
    set {
        guard newValue <= 999 else {
            _foo = 999
            return
        }
        _foo = newValue
    }
}
Jim Driscoll
fuente
No tiene sentido usar dos variables.
Piyush
1
Esto solo usa una propiedad (variable): fooes solo una expresión calculada de _foo, ¡no dejes que la palabra clave "var" te engañe! Significa que hay dos entradas accesibles desde el espacio de nombres privado, pero eso no tiene relación con protected / public y mantiene el valor de fooválido en todo momento. Este es esencialmente el patrón de "vista". El problema que surge con la reescritura a través de didSet, además de tener un período de no ser válido, es que existe un potencial significativo para un bucle infinito ya que está volviendo a ingresar al didSetcontrolador desde adentro didSet.
Jim Driscoll
-8

Ejemplo simplificado:

class Shape {
    var sideLength: Double {
    get {
        return self.sideLength
    }
    set {
        // Implement the setter here.
        self.sideLength = newValue
    }
    }
}

Ejemplo completo

Mira perimetereste ejemplo.

Extracto de: Apple Inc. "El lenguaje de programación Swift". iBooks. https://itun.es/us/jEUH0.l

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
    get {
        return 3.0 * sideLength
    }
    set {
        sideLength = newValue / 3.0
    }
    }

    override func simpleDescription() -> String {
        return "An equilateral triagle with sides of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength”
Mike Rapadas
fuente
3
en este caso, perimetersigue siendo una propiedad calculada. ¿Cómo sobrescribo sideLength sin introducir una propiedad calculada?
bohanl
@bohanl He agregado un ejemplo simplificado usando getyset
Mike Rapadas
6
Su "ejemplo completo" muestra una propiedad calculada, no una propiedad almacenada, y su "ejemplo simplificado" no funciona.
Caleb
Me quedo corregido. Es como si la abstracción de @property (captadores + definidores auto-sintetizados) en Objective-C no se hubiera abstraído en Swift. La ironía ...
Mike Rapadas
6
Su "ejemplo simplificado" es llamar a un captador dentro de un captador de sí mismo. Inf bucle ... choque.
jakenberg