Getters et setters de propriété

194

Avec cette classe simple, je reçois l' avertissement du compilateur

Tenter de modifier / accéder xà son propre setter / getter

et quand je l'utilise comme ça:

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

J'obtiens un EXC_BAD_ACCESS. Comment puis-je faire cela sans soutenir explicitement les ivars?

class Point {

    var x: Int {
        set {
            x = newValue * 2 //Error
        }
        get {
            return x / 2 //Error
        }
    }
    // ...
}
Atomix
la source
1
Faire cela entraînerait également une charge supplémentaire sur le compilateur et consommerait fortement le processeur. Je viens de faire ça: | . C'était l'erreur que je créais. (J'utilisais une aire de jeux)
Honey
Ce comportement est déroutant, au lieu d'utiliser setvous voulez utiliser didSet. Les propriétés se comportent différemment dans Swift que dans Objective-C ou dans d'autres langages lorsque vous les implémentez set. Voir la réponse ci-dessous de @jack et un exemple de didSet@cSquirrel
Paul Solt

Réponses:

232

Setters et Getters s'appliquent à computed properties; ces propriétés n'ont pas de stockage dans l'instance - la valeur du getter est censée être calculée à partir d'autres propriétés d'instance. Dans votre cas, il n'y a pas xà attribuer.

Explicitement: "Comment puis-je faire cela sans soutenir explicitement les ivars". Vous ne pouvez pas - vous aurez besoin de quelque chose pour sauvegarder la propriété calculée. Essaye ça:

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

Plus précisément, dans le 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
la source
106

Les setters / getters dans Swift sont assez différents d'ObjC. La propriété devient une propriété calculée, ce qui signifie qu'elle n'a pas de variable de support telle _xqu'elle le ferait dans ObjC.

Dans le code de solution ci-dessous, vous pouvez voir xTimesTwoque ne stocke rien, mais calcule simplement le résultat à partir de x.

Voir les documents officiels sur les propriétés calculées .

La fonctionnalité souhaitée peut également être Observateurs de propriétés .

Ce dont vous avez besoin c'est:

var x: Int

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

Vous pouvez modifier d'autres propriétés dans le setter / getters, ce à quoi elles sont destinées.

Jack
la source
1
Oui, c'est ce que je lis dans la documentation. J'ai sauté la section des propriétés et il semble qu'il n'y ait aucun moyen de contourner cela, non?
Atomix
Oui, vous aurez besoin d'une autre variable d'instance pour "sauvegarder" la première si vous voulez vraiment le faire. Cependant, les setters ne sont pas destinés à cet effet, vous devriez probablement repenser ce que vous essayez d'accomplir avec cela.
Jack
5
vous pouvez utiliser didSetce qui vous permettra de modifier la valeur immédiatement après sa définition. Rien pour l'obtenir si ...
ObjectiveCsam
1
REMARQUE: si vous souhaitez rétablir la valeur car il ne s'agissait pas d'une entrée non valide, vous devez xrevenir à oldValuedans didSet. Ce changement de comportement est très déroutant en raison des propriétés d'Objective-C, merci!
Paul Solt
105

Vous pouvez personnaliser la valeur définie à l'aide de l'observateur de propriétés. Pour ce faire, utilisez «didSet» au lieu de «set».

class Point {

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

Quant à getter ...

class Point {

var doubleX: Int {
    get {
        return x / 2
    }
}
...
cSquirrel
la source
Comment pourriez-vous initialiser xavec une valeur par défaut dans ce modèle?
i_am_jorf
3
Je vois, ce serait: var x: Int = 0 { didSet { ....
i_am_jorf
31

Pour développer la réponse de GoZoner:

Votre vrai problème ici est que vous appelez récursivement votre getter.

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

Comme le commentaire de code le suggère ci-dessus, vous appelez à l'infini le getter de la propriété x, qui continuera à s'exécuter jusqu'à ce que vous obteniez un code EXC_BAD_ACCESS (vous pouvez voir le spinner dans le coin inférieur droit de l'environnement de jeu de votre Xcode).

Prenons l'exemple de la documentation 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)
        }
    }
}

Remarquez comment la propriété calculée centrale ne se modifie jamais ou ne se renvoie jamais dans la déclaration de la variable.

Abdullah Bakhsh
la source
La règle du pouce est jamais accéder à la propriété elle - même de l' intérieur du getter par exemple get. Parce que cela en déclencherait un autre getqui en déclencherait un autre. . . Ne l'imprimez même pas. Parce que l'impression nécessite également d'obtenir la valeur avant de pouvoir l'imprimer!
Honey
19

Afin de remplacer setteret getterpour les variables rapides, utilisez le code ci-dessous

var temX : Int? 
var x: Int?{

    set(newX){
       temX = newX
    }

    get{
        return temX
    }
}

Nous devons conserver la valeur de variable dans une variable temporaire, car essayer d'accéder à la même variable dont le getter / setter est remplacé entraînera des boucles infinies.

On peut invoquer le setter simplement comme ça

x = 10

Getter sera invoqué lors du tir sous la ligne de code donnée

var newVar = x
Sebin Roy
la source
8

Vous définissez récursivementx avec x. Comme si quelqu'un vous demandait quel âge avez-vous? Et vous répondez "J'ai deux fois mon âge". Ce qui n'a aucun sens.

Vous devez dire que j'ai deux fois l'âge de John ou toute autre variable que vous-même.

les variables calculées dépendent toujours d'une autre variable.


La règle du pouce est jamais accéder à la propriété elle - même de l' intérieur du getter par exemple get. Parce que cela en déclencherait un autre getqui en déclencherait un autre. . . Ne l'imprimez même pas. Parce que l'impression nécessite également d'obtenir la valeur avant de pouvoir l'imprimer!

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

let p1 = Person()

Comme cela donnerait l'avertissement suivant:

Tenter d'accéder à «nom» depuis son propre getter.

L'erreur ressemble à ceci:

entrez la description de l'image ici

Comme alternative, vous voudrez peut-être utiliser didSet. Avec, didSetvous obtiendrez une prise sur la valeur qui a été définie avant et que vous venez de définir. Pour plus voir cette réponse .

Mon chéri
la source
8

Mise à jour: Swift 4

Dans la classe ci-dessous, setter et getter sont appliqués à la variable 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
        }
   }

Création d'objet

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

Getter

print(triangle.perimeter) // invoking getter

Setter

triangle.perimeter = 9.9 // invoking setter
Saranjith
la source
6

Essayez d'utiliser ceci:

var x:Int!

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

C'est essentiellement la réponse de Jack Wu, mais la différence est que dans la réponse de Jack Wu, sa variable x est var x: Int, dans la mienne, ma variable x est comme ceci:, var x: Int!donc tout ce que j'ai fait a été d'en faire un type optionnel.

Epic Defeater
la source
1

Mise à jour pour Swift 5.1

Depuis Swift 5.1, vous pouvez désormais obtenir votre variable sans utiliser le mot clé get . Par exemple:

var helloWorld: String {
"Hello World"
}
atalayasa
la source
Si j'essaye de changer, helloWorld = "macWorld" , ne peut pas assigner à la valeur: 'helloWorld' est une erreur de propriété get-only s'affiche.
McDonal_11
est-il possible d'attribuer de nouvelles valeurs? ou pas ?
McDonal_11
Je ne pense pas que ce soit possible.
atalayasa
Donc, littéralement, var helloWorld: String {"Hello World"} et, laissez helloWorld: String = "Hello World" sont les mêmes?
McDonal_11
Oui, je le pense.
atalayasa
0

Les setters et les getters dans Swift s'appliquent aux propriétés / variables calculées. Ces propriétés / variables ne sont pas réellement stockées en mémoire, mais plutôt calculées en fonction de la valeur des propriétés / variables stockées.

Voir la documentation Swift d'Apple sur le sujet: Déclarations de variables Swift .

jefe2000
la source
0

Voici une réponse théorique. Cela peut être trouvé ici

Une propriété {get set} ne peut pas être une propriété stockée constante. Il doit s'agir d'une propriété calculée et get et set doivent être implémentés.

Talha Ahmad Khan
la source