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.


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
        }
    }
}

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).


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

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
    }
}

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”

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.