Schnelle Programmierung: Getter / Setter in gespeicherter Eigenschaft

102

Wie überschreibe ich den Setter des gespeicherten Eigentums in Swift?

In Obj-C kann ich den Setter überschreiben, aber Swift scheint nicht glücklich darüber zu sein, dass Getter / Setter für gespeicherte Objekte verwendet werden.

Angenommen, ich habe eine CardKlasse mit einer Eigenschaft namens rank. Ich möchte nicht, dass der Client ihm einen ungültigen Wert gibt, daher kann ich in Ziel-C überschreiben, setRankdamit er eine zusätzliche Prüfung durchführt. Aber willSetin Swift scheint es nicht zu helfen, weil newValuees konstant ist und es keinen Sinn macht, es zuzuweisen, rankda der Setter in einer Schleife aufgerufen wird.

Bohanl
quelle
Haben Sie einen Weg gefunden, dies zu tun? Ich brauche diese Art von Funktionalität selbst ...
Mihai Fratu
Ich habe es gefunden. Schauen Sie sich meine Antwort an ...
Mihai Fratu
Was ist mit didGet oder analog?
FNC12

Antworten:

107

OK. Lesen durch Apples Dokumentation auf Swift fand ich diese :

Wenn Sie einer Eigenschaft innerhalb ihres eigenen didSet-Beobachters einen Wert zuweisen, ersetzt der neue Wert, den Sie zuweisen, den gerade festgelegten Wert.

Alles was Sie tun müssen ist Folgendes:

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
quelle
Was ist mit didGet oder analog?
FNC12
Ich bin mir nicht sicher, ob ich deine Frage verstehe. Können Sie bitte genauer sein?
Mihai Fratu
Ich muss einen Code aufrufen, bevor ich ihn bekomme. Ich konnte dies in obj-c ausführen, aber ich kann nicht sehen, wie dies in Swift ausgeführt werden soll. Ich sehe nur zwei Eigenschaften: eine ist öffentlich und eine ist privat, public ruft meinen Code auf und gibt den Wert der privaten Eigenschaft zurück. Deshalb habe ich nach didGet
fnc12
Sie können nur einen Getter für eine berechnete Eigenschaft haben. Zum Beispielvar rankTimesTwo: Int { get { return rank * 2 } }
Mihai Fratu
2
Klappt wunderbar! Beachten Sie, dass es nicht aufgerufen wird, wenn Sie die Eigenschaft in init () festlegen
Christoph
35

Sie können get/ setfür eine gespeicherte Eigenschaft nicht überschreiben, aber Sie können Eigenschaftsbeobachter willSet/ verwenden didSet.

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

Die Standardparameternamen sind newValuefür willSetund oldValuefür didSet, oder Sie können sie selbst wie in benennen willSet(newTotalSteps).

Joseph Mark
quelle
Das funktioniert. Aber löst mein Problem nicht, vielleicht war ich in meiner ursprünglichen Frage nicht klar genug.
Bohanl
Angenommen, ich habe eine CardKlasse mit einer Eigenschaft namens rank. Ich möchte nicht, dass der Client ihm einen Wert gibt, daher kann ich in Ziel-C überschreiben, setRankdamit er eine zusätzliche Prüfung durchführt. Aber willSetin Swift scheint es nicht zu helfen, weil newValuees konstant ist und es keinen Sinn macht, es zuzuweisen, rankda der Setter in einer Schleife aufgerufen wird.
Bohanl
2
Ich bin mir nicht ganz sicher, was Sie meinen, aber können Sie nicht didSetüberprüfen, ranknachdem es eingestellt wurde, und wenn es die Validierung nicht besteht, setzen Sie es auf etwas anderes zurück, z oldValue.
Joseph Mark
9

get und set sind für berechnete Eigenschaften (sie haben keinen Hintergrundspeicher). (Meiner Meinung nach ist das Schlüsselwort 'var' hier verwirrend)

  • willSet und didSet werden für eine Instanzvariable aufgerufen (Verwenden Sie didSet, um alle Änderungen zu überschreiben.)
  • set und get sind nur für berechnete Eigenschaften
user3675131
quelle
9

Wenn Sie didSet nicht verwenden möchten, da das Problem besteht, dass der Wert der Eigenschaft vorübergehend falsch ist, sollten Sie eine berechnete Eigenschaft darum wickeln.

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

Oder:

private var _foo:Int = 0
var foo:Int {
    get {
        return _foo
    }
    set {
        guard newValue <= 999 else {
            _foo = 999
            return
        }
        _foo = newValue
    }
}
Jim Driscoll
quelle
Es ist nicht sinnvoll, zwei Variablen zu verwenden.
Piyush
1
Dies verwendet nur eine Eigenschaft (Variable): fooist nur ein berechneter Ausdruck von _foo. Lassen Sie sich nicht vom Schlüsselwort "var" täuschen! Dies bedeutet, dass zwei Einträge über den privaten Namespace zugänglich sind. Dies hat jedoch keinen Einfluss auf protected / public und behält den Wert von foojederzeit bei. Dies ist im Wesentlichen das "Ansicht" -Muster. Das Problem beim Umschreiben über didSet, zusätzlich zu einer ungültigen Zeitspanne, besteht darin, dass ein erhebliches Potenzial für eine Endlosschleife besteht, da Sie den didSetHandler von innen erneut eingeben didSet.
Jim Driscoll
-8

Vereinfachtes Beispiel:

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

Vollständiges Beispiel

Schauen Sie sich perimeterdieses Beispiel an.

Auszug aus: Apple Inc. "Die schnelle Programmiersprache". 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
quelle
3
In diesem Fall perimeterhandelt es sich immer noch um eine berechnete Eigenschaft. Wie überschreibe ich sideLength, ohne eine berechnete Eigenschaft einzuführen?
Bohanl
@ Bohanl Ich habe ein vereinfachtes Beispiel mit getundset
Mike Rapadas
6
Ihr "vollständiges Beispiel" zeigt eine berechnete Eigenschaft, keine gespeicherte Eigenschaft, und Ihr "vereinfachtes Beispiel" funktioniert nicht.
Caleb
Ich stehe korrigiert. Es ist, als ob die Abstraktion von @property (automatisch synthetisierte Getter + Setter) in Objective-C in Swift nicht abgelenkt worden wäre. Die Ironie ...
Mike Rapadas
6
Ihr "vereinfachtes Beispiel" nennt einen Getter in einem Getter von sich. Inf-Schleife ... Absturz.
Jakbergberg