Getters e setters de propriedades

194

Com esta classe simples, estou recebendo o aviso do compilador

Tentativa de modificar / acessar xdentro de seu próprio setter / getter

e quando eu uso assim:

var p: point = Point()
p.x = 12

Eu recebo um EXC_BAD_ACCESS. Como posso fazer isso sem ivars de apoio explícitos?

class Point {

    var x: Int {
        set {
            x = newValue * 2 //Error
        }
        get {
            return x / 2 //Error
        }
    }
    // ...
}
Atomix
fonte
1
Fazer isso também colocaria carga extra no compilador e consumiria a CPU vigorosamente. Acabei de fazer isso: | . Este foi o erro que eu estava criando. (Eu estava usando playground)
Mel
Esse comportamento é confuso, em vez de usar o que setvocê deseja usar didSet. As propriedades se comportam de maneira diferente no Swift que no Objective-C ou em outros idiomas quando você implementa set. Veja a resposta abaixo de @jack e um exemplo de didSetpartir @cSquirrel
Paul Solt

Respostas:

232

Setters e Getters se aplicam a computed properties; essas propriedades não têm armazenamento na instância - o valor do getter deve ser calculado a partir de outras propriedades da instância. No seu caso, não há xcomo atribuir.

Explicitamente: "Como posso fazer isso sem ivars de apoio explícitos". Você não pode - precisará de algo para fazer backup da propriedade computada. Tente o seguinte:

class Point {
  private var _x: Int = 0             // _x -> backingX
  var x: Int {
    set { _x = 2 * newValue }
    get { return _x / 2 }
  }
}

Especificamente, no Swift REPL:

 15> var pt = Point()
pt: Point = {
  _x = 0
}
 16> pt.x = 10
 17> pt
$R3: Point = {
  _x = 20
}
 18> pt.x
$R4: Int = 10
GoZoner
fonte
106

Setters / getters no Swift são bem diferentes do ObjC. A propriedade se torna uma propriedade calculada, o que significa que não possui uma variável de suporte, como _xteria no ObjC.

No código da solução abaixo, você pode ver xTimesTwoque não armazena nada, mas simplesmente calcula o resultado de x.

Consulte Documentos oficiais sobre propriedades computadas .

A funcionalidade que você deseja também pode ser Observadores de propriedades .

O que você precisa é:

var x: Int

var xTimesTwo: Int {
    set {
       x = newValue / 2
    }
    get {
        return x * 2
    }
}

Você pode modificar outras propriedades dentro do setter / getters, para que servem.

Jack
fonte
1
Sim, é isso que estou lendo na documentação. Eu pulei a seção de propriedades e parece que não há maneira de contornar isso, certo?
precisa
Sim, você precisará de outra variável de instância para "voltar" a primeira, se realmente quiser fazer isso. No entanto, os levantadores não são para esse fim, você provavelmente deve repensar o que está tentando realizar com isso.
Jack
5
você pode usar o didSetque permitirá alterar o valor imediatamente após ser definido. Nada para obter ...
ObjectiveCsam
1
NOTA: Se você quiser reverter o valor porque era uma entrada inválida, você precisa conjunto xde volta para oldValueno didSet. Essa mudança de comportamento é muito confusa, proveniente das propriedades do Objective-C, obrigado!
Paul Solt
105

Você pode personalizar o valor configurado usando o observador de propriedades. Para fazer isso, use 'didSet' em vez de 'set'.

class Point {

var x: Int {
    didSet {
        x = x * 2
    }
}
...

Quanto a getter ...

class Point {

var doubleX: Int {
    get {
        return x / 2
    }
}
...
cSquirrel
fonte
Como você pode inicializar xcom um valor padrão nesse padrão?
precisa saber é o seguinte
3
Eu vejo, seria: var x: Int = 0 { didSet { ....
precisa saber é o seguinte
31

Para elaborar a resposta do GoZoner:

Seu problema real aqui é que você está chamando recursivamente o seu parceiro.

var x:Int
    {
        set
        {
            x = newValue * 2 // This isn't a problem
        }
        get {
            return x / 2 // Here is your real issue, you are recursively calling 
                         // your x property's getter
        }
    }

Como o comentário do código sugere acima, você está chamando infinitamente o getter da propriedade x, que continuará sendo executado até obter um código EXC_BAD_ACCESS (você pode ver o botão giratório no canto inferior direito do ambiente de playground do seu Xcode).

Considere o exemplo da documentação do Swift :

struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct AlternativeRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

Observe como a propriedade computada ao centro nunca se modifica ou retorna na declaração da variável.

Abdullah Bakhsh
fonte
A regra do polegar é não acessar a propriedade em si a partir de dentro do getter ie get. Porque isso desencadearia outro getque desencadearia outro. . . Nem imprima. Porque a impressão também exige 'obter' o valor antes que ela possa ser impressa!
Querida
19

Para substituir settere getterpara variáveis ​​rápidas, use o código abaixo

var temX : Int? 
var x: Int?{

    set(newX){
       temX = newX
    }

    get{
        return temX
    }
}

Precisamos manter o valor da variável em uma variável temporária, pois tentar acessar a mesma variável cujo getter / setter está sendo substituído resultará em loops infinitos.

Podemos invocar o setter simplesmente assim

x = 10

O Getter será chamado ao disparar abaixo da linha de código fornecida

var newVar = x
Sebin Roy
fonte
8

Você está definindo recursivamentex com x. Como se alguém lhe perguntasse quantos anos você tem? E você responde: "Tenho o dobro da minha idade". O que não tem sentido.

Você deve dizer que tenho o dobro da idade de John ou qualquer outra variável que não seja você.

variáveis ​​computadas são sempre dependentes de outra variável.


A regra do polegar é não acessar a propriedade em si a partir de dentro do getter ie get. Porque isso desencadearia outro getque desencadearia outro. . . Nem imprima. Porque a impressão também exige 'obter' o valor antes que ela possa ser impressa!

struct Person{
    var name: String{
        get{
            print(name) // DON'T do this!!!!
            return "as"
        }
        set{
        }
    }
}

let p1 = Person()

Como isso daria o seguinte aviso:

Tentativa de acessar 'nome' de dentro do seu próprio getter.

O erro parece vago assim:

insira a descrição da imagem aqui

Como alternativa, você pode querer usar didSet. Com didSetvocê manterá o valor que foi definido antes e acabou de ser definido. Para mais, veja esta resposta .

Mel
fonte
8

Atualização: Swift 4

Na classe abaixo, setter e getter são aplicados à variável sideLength

class Triangle: {
    var sideLength: Double = 0.0

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

    var perimeter: Double {
        get { // getter
            return 3.0 * sideLength
        }
        set(newValue) { //setter
            sideLength = newValue / 4.0
        }
   }

Criando objeto

var triangle = Triangle(sideLength: 3.9, name: "a triangle")

Getter

print(triangle.perimeter) // invoking getter

Normatizador

triangle.perimeter = 9.9 // invoking setter
Saranjith
fonte
6

Tente usar isto:

var x:Int!

var xTimesTwo:Int {
    get {
        return x * 2
    }
    set {
        x = newValue / 2
    }
}

Essa é basicamente a resposta de Jack Wu, mas a diferença é que, na resposta de Jack Wu, sua variável x é var x: Int, na minha, minha variável x é assim: var x: Int!então tudo o que fiz foi torná-la um tipo opcional.

Epic Defeater
fonte
1

Atualização para o Swift 5.1

A partir do Swift 5.1, agora você pode obter sua variável sem usar a palavra-chave get . Por exemplo:

var helloWorld: String {
"Hello World"
}
atalayasa
fonte
Se eu tentar alterar, helloWorld = "macWorld" , Não é possível atribuir ao valor: 'helloWorld' é um erro de propriedade get-only sendo exibido.
McDonal_11
é possível atribuir novos valores? ou não ?
McDonal_11 4/07/19
Eu não acho que isso é possível.
Atalayasa
Então, literalmente, var helloWorld: String {"Hello World"} e, deixe helloWorld: String = "Hello World" são iguais?
McDonal_11 04/07/19
Acho que sim.
atalayasa
0

Setters e getters no Swift se aplicam a propriedades / variáveis ​​calculadas. Na verdade, essas propriedades / variáveis ​​não são armazenadas na memória, mas computadas com base no valor das propriedades / variáveis ​​armazenadas.

Consulte a documentação Swift da Apple sobre o assunto: Swift Variable Declarations .

jefe2000
fonte
0

Aqui está uma resposta teórica. Isso pode ser encontrado aqui

Uma propriedade {get set} não pode ser uma propriedade armazenada constante. Deve ser uma propriedade calculada e get e set devem ser implementados.

Talha Ahmad Khan
fonte