Obiekty pobierające i ustawiające


194

Dzięki tej prostej klasie otrzymuję ostrzeżenie kompilatora

Próba modyfikacji / dostępu xw ramach własnego setera / gettera

a kiedy używam tego w ten sposób:

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

Dostaję EXC_BAD_ACCESS. Jak mogę to zrobić bez wyraźnego tworzenia kopii zapasowych?

class Point {

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

1
Takie działanie spowodowałoby również dodatkowe obciążenie kompilatora i energicznie zużyłoby procesor. Właśnie to zrobiłem: | . To był błąd, który tworzyłem. (Korzystałem z placu zabaw)
Honey

To zachowanie jest mylące, zamiast używania, setktórego chcesz używać didSet. Po wdrożeniu właściwości zachowują się inaczej w Swift niż Objective-C lub innych językach set. Zobacz odpowiedź poniżej z @jack i przykład didSetz @cSquirrel
Paul Solt

Odpowiedzi:


232

Settery i Getters mają zastosowanie do computed properties; takie właściwości nie mają pamięci w instancji - wartość z gettera ma być obliczona na podstawie innych właściwości instancji. W twoim przypadku nie ma xprzypisania.

Jawnie: „Jak mogę to zrobić bez wyraźnego tworzenia kopii zapasowych”. Nie można - trzeba coś do tworzenia kopii zapasowych nieruchomości komputerowej. Spróbuj tego:

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

W szczególności w 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

106

Settery / gettery w Swift są zupełnie inne niż ObjC. Właściwość staje się własnością obliczoną, co oznacza, że nie ma zmiennej _xbazowej, takiej jak w ObjC.

W poniższym kodzie rozwiązania widać, że xTimesTwonic nie przechowuje, ale po prostu oblicza wynik x.

Zobacz oficjalne dokumenty dotyczące obliczonych właściwości .

Funkcjonalność, którą chcesz, może być również obserwatorem właściwości .

Potrzebujesz:

var x: Int

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

Możesz modyfikować inne właściwości w setter / getters, do czego są przeznaczone.


1
Tak, właśnie to czytam w dokumentacji. Przeskoczyłem sekcję nieruchomości i wygląda na to, że nie ma innego wyjścia, prawda?
Atomix

Tak, będziesz potrzebować innej zmiennej instancji, aby „cofnąć” pierwszą, jeśli naprawdę chcesz to zrobić. Jednak setery nie są przeznaczone do tego celu, prawdopodobnie powinieneś ponownie przemyśleć, co próbujesz osiągnąć dzięki temu.
Jack

5
możesz użyć, didSetco pozwoli ci zmienić wartość natychmiast po jej ustawieniu. Nie ma jednak nic do zdobycia ...
ObjectiveCsam

1
UWAGA: Jeśli chcesz, aby przywrócić wartość, ponieważ był nieważny wejścia, to trzeba ustawić xplecami do oldValuew didSet. Ta zmiana w zachowaniu jest bardzo myląca, ponieważ pochodzi od właściwości Objective-C, dzięki!
Paul Solt

105

Można dostosować ustawioną wartość za pomocą obserwatora właściwości. Aby to zrobić, użyj „didSet” zamiast „set”.

class Point {

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

Co do gettera ...

class Point {

var doubleX: Int {
    get {
        return x / 2
    }
}
...

Jak można zainicjować xwartość domyślną w tym wzorcu?
i_am_jorf 21.04.16

3
Widzę, byłoby: var x: Int = 0 { didSet { ....
i_am_jorf 21.04.16

31

Aby rozwinąć odpowiedź na temat GoZonera:

Twoim prawdziwym problemem jest to, że rekurencyjnie dzwonisz do swojego gettera.

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

Jak sugeruje powyższy komentarz do kodu, nieskończenie wywołujesz moduł pobierający właściwość x, który będzie kontynuowany do momentu otrzymania kodu EXC_BAD_ACCESS (możesz zobaczyć pokrętło w prawym dolnym rogu środowiska zabaw Xcode).

Rozważ przykład z dokumentacji 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)
        }
    }
}

Zauważ, że właściwość obliczona przez środek nigdy nie zmienia się ani nie zwraca siebie w deklaracji zmiennej.


Zasadą kciuka jest, aby nigdy nie uzyskiwać dostępu do samej właściwości z poziomu gettera, tj get. Ponieważ to uruchomiłoby inną, getktóra uruchomiłaby inną. . . Nawet go nie drukuj. Ponieważ drukowanie wymaga również „uzyskania” wartości, zanim będzie można ją wydrukować!
Honey,

19

W celu zastąpienia setteri getterdla szybkich zmiennych użyj poniższego kodu

var temX : Int? 
var x: Int?{

    set(newX){
       temX = newX
    }

    get{
        return temX
    }
}

Musimy zachować wartość zmiennej w zmiennej tymczasowej, ponieważ próba uzyskania dostępu do tej samej zmiennej, której getter / setter jest zastępowany, spowoduje nieskończone pętle.

Możemy przywołać setera w ten sposób

x = 10

Getter zostanie wywołany przy odpaleniu poniżej podanego wiersza kodu

var newVar = x

8

Jesteś rekursywnie definiowania xz x. Jakby ktoś zapytał, ile masz lat? A ty odpowiadasz „Jestem dwa razy starszy”. Co nie ma sensu.

Musisz powiedzieć, że jestem dwa razy starszy od Johna lub jakąkolwiek inną zmienną oprócz ciebie .

zmienne obliczane są zawsze zależne od innej zmiennej.


Zasadą kciuka jest, aby nigdy nie uzyskiwać dostępu do samej właściwości z poziomu gettera, tj get. Ponieważ to uruchomiłoby inną, getktóra uruchomiłaby inną. . . Nawet go nie drukuj. Ponieważ drukowanie wymaga również „uzyskania” wartości, zanim będzie można ją wydrukować!

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

let p1 = Person()

To dałoby następujące ostrzeżenie:

Próba dostępu do „name” z poziomu własnego gettera.

Błąd wygląda niejasno:

wprowadź opis zdjęcia tutaj

Alternatywnie możesz użyć didSet. Dzięki temu didSetuzyskasz wartość ustawioną wcześniej i dopiero ustawioną na. Aby uzyskać więcej, zobacz tę odpowiedź .


8

Aktualizacja: Swift 4

W poniższej klasie seter i getter są stosowane do zmiennej 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
        }
   }

Tworzenie obiektu

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

Rębacz

print(triangle.perimeter) // invoking getter

Seter

triangle.perimeter = 9.9 // invoking setter

6

Spróbuj użyć tego:

var x:Int!

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

Jest to w zasadzie odpowiedź Jacka Wu, ale różnica polega na tym, że w odpowiedzi Jacka Wu jego zmienna x jest var x: Int, w mojej, moja zmienna x jest taka: var x: Int!więc wszystko, co zrobiłem, to uczynienie z niej opcjonalnego typu.


1

Aktualizacja dla Swift 5.1

Począwszy od wersji Swift 5.1, możesz teraz uzyskać zmienną bez użycia słowa kluczowego get . Na przykład:

var helloWorld: String {
"Hello World"
}

Jeśli spróbuję zmienić, helloWorld = "macWorld" , Nie można przypisać do wartości: „helloWorld” oznacza błąd właściwości „tylko pobierz”.
McDonal_11

czy możliwe jest przypisanie nowych wartości? albo nie ?
McDonal_11

Nie sądzę, żeby to było możliwe.
atalayasa

Więc dosłownie, var helloWorld: String {„Hello World”} i, niech helloWorld: String = „Hello World” są takie same?
McDonal_11

Tak myślę.
atalayasa

0

Settery i gettery w Swift dotyczą obliczonych właściwości / zmiennych. Te właściwości / zmienne nie są faktycznie przechowywane w pamięci, ale są obliczane na podstawie wartości przechowywanych właściwości / zmiennych.

Zobacz dokumentację Apple Swift na ten temat: Swift Variable Declarations .


0

Oto teoretyczna odpowiedź. Można to znaleźć tutaj

Właściwość {get set} nie może być stałą przechowywaną właściwością. Powinna to być właściwość obliczona, a zarówno get, jak i set powinny być zaimplementowane.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.