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.