Programação rápida: getter / setter na propriedade armazenada

102

Como faço para sobrescrever o setter da propriedade armazenada no Swift?

No Obj-C, posso sobrescrever seu setter, mas o Swift não parece estar feliz com getter / setters sendo usados ​​para propriedade armazenada.

Digamos que eu tenha uma Cardclasse com uma propriedade chamada rank. Não quero que o cliente dê a ele nenhum valor inválido, portanto, no objetivo-C, posso sobrescrever setRankpara que ele execute uma verificação adicional. Mas willSetem Swift não parece ajudar porque newValueé constante e não faz sentido atribuir rankporque setter será chamado em um loop.

Bohanl
fonte
Você encontrou uma maneira de fazer isso? Eu mesmo preciso desse tipo de funcionalidade ...
Mihai Fratu
Eu encontrei. Confira minha resposta ...
Mihai Fratu
E sobre didGet ou analógico?
fnc12

Respostas:

107

Está bem. Lendo a documentação da Apple no Swift, descobri o seguinte :

Se você atribuir um valor a uma propriedade dentro de seu próprio observador didSet, o novo valor atribuído substituirá o que acabou de ser definido.

Então, tudo que você precisa fazer é o seguinte:

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
fonte
E sobre didGet ou analógico?
fnc12
Não tenho certeza se entendi sua pergunta. Por favor você pode ser mais especifico?
Mihai Fratu
Preciso invocar algum código antes de obter. Consegui fazer isso em obj-c, mas não consigo ver como fazer no Swift. A única coisa que vejo é usar duas propriedades: uma é pública e outra é privada, public invoca meu código e retorna o valor da propriedade privada. É por isso que perguntei sobre didGet
fnc12
Você só pode ter um getter para uma propriedade computada. Por exemplovar rankTimesTwo: Int { get { return rank * 2 } }
Mihai Fratu
2
Funciona como um encanto! Cuidado, ele não será chamado ao definir a propriedade em init ()
Christoph
35

Você não pode substituir get/ setpor uma propriedade armazenada, mas pode usar observadores de propriedade willSet/ didSet:

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

Os nomes dos parâmetros padrão são newValuepara willSete oldValuepara didSet, ou você mesmo pode nomeá-los como em willSet(newTotalSteps).

Joseph Mark
fonte
Isso funciona. Mas não resolve meu problema, talvez eu não tenha sido claro o suficiente em minha pergunta original.
bohanl de
Digamos que eu tenha uma Cardclasse com uma propriedade chamada rank. Não quero que o cliente atribua nenhum valor, portanto, no objetivo-C, posso sobrescrever setRankpara que ele execute uma verificação adicional. Mas willSetem Swift não parece ajudar porque newValueé constante e não faz sentido atribuir rankporque setter será chamado em um loop.
bohanl de
2
Não tenho certeza do que você quer dizer, mas você não poderia usar didSetpara verificar rankdepois de ser definido e se falhar na validação, redefina-o para outra coisa, por exemplo oldValue?
Joseph Mark de
9

get e set são para propriedades computadas (eles não têm nenhum armazenamento de apoio). (Na minha opinião, a palavra-chave 'var' é confusa aqui)

  • willSet e didSet são chamados para uma variável de instância (Use didSet para substituir quaisquer alterações)
  • set e get são puramente para propriedades computadas
user3675131
fonte
9

Se você não quiser usar didSet, que apresenta o problema de o valor da propriedade estar temporariamente errado, você deve envolver uma propriedade computada em torno dele.

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

Ou:

private var _foo:Int = 0
var foo:Int {
    get {
        return _foo
    }
    set {
        guard newValue <= 999 else {
            _foo = 999
            return
        }
        _foo = newValue
    }
}
Jim Driscoll
fonte
Não faz sentido usar duas variáveis.
Piyush
1
Isso está usando apenas uma propriedade (variável): fooé apenas uma expressão calculada de _foo, não se deixe enganar pela palavra-chave "var"! Isso significa que há duas entradas acessíveis a partir do namespace privado, mas isso não tem relação com protected / public e mantém o valor de fooválido em todos os momentos. Este é essencialmente o padrão de "visualização". O problema que você obtém com a reescrita via didSet, além de ter um período de invalidação, é que há um potencial significativo para um loop infinito, já que você está reinserindo o didSetmanipulador por dentro didSet.
Jim Driscoll
-8

Exemplo simplificado:

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

Exemplo completo

Confira perimeterneste exemplo.

Trecho de: Apple Inc. “The Swift Programming Language”. 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
fonte
3
neste caso, perimeterainda é uma propriedade computada. Como sobrescrevo sideLength sem introduzir uma propriedade computada?
bohanl
@bohanl Eu adicionei um exemplo simplificado usando geteset
Mike Rapadas
6
Seu "exemplo completo" mostra uma propriedade computada, não uma propriedade armazenada, e seu "exemplo simplificado" não funciona.
Caleb
Eu estou corrigido. É como se a abstração de @property (getters + setters auto-sintetizados) em Objective-C não tivesse sido abstraída em Swift. A ironia ...
Mike Rapadas
6
Seu "exemplo simplificado" é chamar um getter dentro de um getter de si mesmo. Inf loop ... crash.
jakenberg