Szybkie programowanie: getter / setter w przechowywanej właściwości

102

Jak nadpisać setter przechowywanej właściwości w Swift?

W Obj-C mogę nadpisać jego metodę ustawiającą, ale Swift nie wydaje się być zadowolony z tego, że metody pobierające / ustawiające są używane do przechowywanych właściwości.

Powiedzmy, że mam Cardklasę z właściwością o nazwie rank. Nie chcę, aby klient nadał mu jakąkolwiek nieprawidłową wartość, dlatego w celu-C mogę nadpisać, setRankaby wykonać dodatkowe sprawdzenie. Ale willSetw Swift nie wydaje się pomagać, ponieważ newValuejest stały i nie ma sensu przypisywać, rankponieważ ustawiający zostanie wywołany w pętli.

bohanl
źródło
Czy znalazłeś sposób, aby to zrobić? Sam potrzebuję takiej funkcjonalności ...
Mihai Fratu
Znalazłem to. Sprawdź moją odpowiedź ...
Mihai Fratu
A co z didGet lub analogiem?
fnc12

Odpowiedzi:

107

Dobrze. Czytając dokumentację Apples na Swift, znalazłem to :

Jeśli przypiszesz wartość do właściwości w jej własnym obserwatorze didSet, nowa wartość, którą przypiszesz, zastąpi tę, która została właśnie ustawiona.

Więc wszystko, co musisz zrobić, to:

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
źródło
A co z didGet lub analogiem?
fnc12
Nie jestem pewien, czy rozumiem twoje pytanie. Czy możesz być bardziej szczegółowy, proszę?
Mihai Fratu
Muszę wywołać jakiś kod przed uzyskaniem. Udało mi się to zrobić w obj-c, ale nie widzę, jak to zrobić w języku Swift. Jedyne, co widzę, to użycie dwóch właściwości: jedna jest publiczna, a druga prywatna, public wywołuje mój kod i zwraca wartość właściwości prywatnej. Dlatego zapytałem o didGet
fnc12
Możesz mieć tylko metodę pobierającą dla obliczonej właściwości. Na przykładvar rankTimesTwo: Int { get { return rank * 2 } }
Mihai Fratu
2
Działa jak marzenie! Uwaga, nie zostanie wywołane podczas ustawiania właściwości w init ()
Christoph
35

Nie możesz nadpisać get/ setdla przechowywanej właściwości, ale możesz użyć obserwatorów właściwości willSet/ didSet:

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

Domyślne nazwy parametrów to newValuefor willSeti oldValuefor didSetlub możesz je nazwać samodzielnie, jak w willSet(newTotalSteps).

Joseph Mark
źródło
To działa. Ale to nie rozwiązuje mojego problemu, może nie byłem wystarczająco jasny w moim pierwotnym pytaniu.
bohanl
Powiedzmy, że mam Cardklasę z właściwością o nazwie rank. Nie chcę, aby klient nadawał mu jakąkolwiek wartość, dlatego w celu-C mogę nadpisać, setRankaby wykonać dodatkowe sprawdzenie. Ale willSetw Swift nie wydaje się pomagać, ponieważ newValuejest stały i nie ma sensu przypisywać, rankponieważ ustawiający zostanie wywołany w pętli.
bohanl
2
Nie jestem do końca pewien, co masz na myśli, ale nie mógłbyś użyć didSetdo sprawdzenia rankpo ustawieniu, a jeśli nie powiedzie się walidacja, zresetuj ją do czegoś innego, np. oldValue?
Joseph Mark,
9

get i set są dla obliczonych właściwości (nie mają żadnego magazynu zapasowego). (Moim zdaniem słowo kluczowe „var” jest tutaj mylące)

  • willSet i didSet są wywoływane dla zmiennej instancji (użyj didSet, aby nadpisać wszelkie zmiany)
  • set i get służą wyłącznie do obliczania właściwości
user3675131
źródło
9

Jeśli nie chcesz używać didSet, który ma problem polegający na tym, że wartość właściwości jest tymczasowo nieprawidłowa, powinieneś owinąć wokół niego obliczoną właściwość.

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

Lub:

private var _foo:Int = 0
var foo:Int {
    get {
        return _foo
    }
    set {
        guard newValue <= 999 else {
            _foo = 999
            return
        }
        _foo = newValue
    }
}
Jim Driscoll
źródło
Używanie dwóch zmiennych nie ma sensu.
Piyush
1
To jest użycie tylko jednej właściwości (zmiennej): foojest to tylko obliczone wyrażenie _foo, nie daj się zwieść słowu kluczowemu "var"! Oznacza to, że istnieją dwa wpisy dostępne z prywatnej przestrzeni nazw, ale nie ma to wpływu na wartość protected / public i przez cały czas zachowuje fooważność. Zasadniczo jest to wzorzec „widoku”. Problem , który pojawia się przy przepisywaniu przez didSet, oprócz tego, że ma okres nieważności, polega na tym, że istnieje znaczny potencjał nieskończonej pętli, ponieważ ponownie wchodzisz do programu didSetobsługi od wewnątrz didSet.
Jim Driscoll
-8

Uproszczony przykład:

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

Pełny przykład

Sprawdzić perimeter w tym przykładzie.

Fragment z: Apple Inc. „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”
Mike Rapadas
źródło
3
w tym przypadku perimeternadal jest obliczoną własnością. Jak nadpisać sideLength bez wprowadzania obliczonej właściwości?
bohanl
@bohanl Dodałem uproszczony przykład przy użyciu getiset
Mike Rapadas,
6
Twój „pełny przykład” przedstawia obliczoną właściwość, a nie przechowywaną właściwość, a „uproszczony przykład” nie działa.
Caleb
Poprawiono mnie. To tak, jakby abstrakcja @property (auto-syntetyzowane metody pobierające + ustawiające) w Objective-C została nieobjęta w Swift. Ironia ...
Mike Rapadas
6
„Uproszczony przykład” to wywołanie metody pobierającej w ramach samego gettera. Pętla inf ... awaria.
jakenberg