Property Getter und Setter

194

Mit dieser einfachen Klasse erhalte ich die Compiler- Warnung

Versuch, xinnerhalb seines eigenen Setters / Getters zu ändern / darauf zuzugreifen

und wenn ich es so benutze:

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

Ich bekomme eine EXC_BAD_ACCESS. Wie kann ich dies tun, ohne Ivars explizit zu unterstützen?

class Point {

    var x: Int {
        set {
            x = newValue * 2 //Error
        }
        get {
            return x / 2 //Error
        }
    }
    // ...
}
Atomix
quelle
1
Dies würde auch den Compiler zusätzlich belasten und die CPU stark beanspruchen. Ich habe das gerade gemacht: | . Dies war der Fehler, den ich erstellt habe. (Ich benutzte einen Spielplatz)
Honey
Dieses Verhalten ist verwirrend, anstatt zu verwenden, was setSie verwenden möchten didSet. Eigenschaften verhalten sich bei der Implementierung in Swift anders als in Objective-C oder anderen Sprachen set. Siehe die Antwort unten von @jack und ein Beispiel didSetvon @cSquirrel
Paul Solt

Antworten:

232

Setter und Getter gelten für computed properties; Solche Eigenschaften haben keinen Speicher in der Instanz - der Wert vom Getter soll aus anderen Instanzeigenschaften berechnet werden. In Ihrem Fall ist keine xzuzuweisen.

Explizit: "Wie kann ich dies tun, ohne Ivars explizit zu unterstützen?" Sie können nicht - Sie benötigen etwas, um die berechnete Eigenschaft zu sichern. Versuche dies:

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

Insbesondere in der 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
quelle
106

Setter / Getter in Swift sind ganz anders als ObjC. Die Eigenschaft wird zu einer berechneten Eigenschaft das heißt , es ist nicht einen Träger Variable wie _xwie es wäre , in ObjC.

In der Code - Lösung unten können Sie sehen , das xTimesTwoist nicht alles speichern, sondern einfach berechnet das Ergebnis aus x.

Siehe Offizielle Dokumente zu berechneten Eigenschaften .

Die gewünschte Funktionalität kann auch Property Observers sein .

Was Sie brauchen ist:

var x: Int

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

Sie können andere Eigenschaften innerhalb des Setters / Getters ändern, wofür sie bestimmt sind.

Jack
quelle
1
Ja, das lese ich in der Dokumentation. Ich habe den Immobilienbereich übersprungen und es scheint, dass daran kein Weg vorbei ist, oder?
Atomix
Ja, Sie benötigen eine andere Instanzvariable, um die erste zu "sichern", wenn Sie dies wirklich tun möchten. Setter sind jedoch nicht für diesen Zweck gedacht. Sie sollten sich wahrscheinlich überlegen, was Sie damit erreichen möchten.
Jack
5
Sie können verwenden didSet, um den Wert unmittelbar nach dem Festlegen zu ändern. Nichts für das Get obwohl ...
ObjectiveCsam
1
HINWEIS: Wenn Sie den Wert zurücksetzen wollen , weil es ungültige Eingabe war, den Sie einstellen bräuchten , xum wieder oldValuein die didSet. Diese Verhaltensänderung ist aufgrund der Objective-C-Eigenschaften sehr verwirrend, danke!
Paul Solt
105

Sie können den eingestellten Wert mit dem Eigenschaftsbeobachter anpassen. Verwenden Sie dazu 'didSet' anstelle von 'set'.

class Point {

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

Wie für Getter ...

class Point {

var doubleX: Int {
    get {
        return x / 2
    }
}
...
cSquirrel
quelle
Wie können Sie xmit einem Standardwert in diesem Muster initialisieren ?
i_am_jorf
3
Ich verstehe, es wäre : var x: Int = 0 { didSet { ....
i_am_jorf
31

Um die Antwort von GoZoner zu erläutern:

Ihr eigentliches Problem hierbei ist, dass Sie Ihren Getter rekursiv anrufen.

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

Wie der obige Codekommentar andeutet, rufen Sie den Getter der x-Eigenschaft unendlich auf, der so lange ausgeführt wird, bis Sie einen EXC_BAD_ACCESS-Code erhalten (Sie können den Spinner in der unteren rechten Ecke der Spielplatzumgebung Ihres Xcodes sehen).

Betrachten Sie das Beispiel aus der Swift-Dokumentation :

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

Beachten Sie, dass die berechnete Eigenschaft center in der Deklaration der Variablen niemals geändert oder zurückgegeben wird.

Abdullah Bakhsh
quelle
Die Regel des Daumens ist nie die Eigenschaft zugreifen , sich von innerhalb der Getter dh get. Denn das würde einen anderen getauslösen, der einen anderen auslösen würde. . . Drucken Sie es nicht einmal aus. Weil beim Drucken auch der Wert "abgerufen" werden muss, bevor er gedruckt werden kann!
Honey
19

Verwenden Sie zum Überschreiben setterund getterfür schnelle Variablen den unten angegebenen Code

var temX : Int? 
var x: Int?{

    set(newX){
       temX = newX
    }

    get{
        return temX
    }
}

Wir müssen den Wert der Variablen in einer temporären Variablen behalten, da der Versuch, auf dieselbe Variable zuzugreifen, deren Getter / Setter überschrieben wird, zu Endlosschleifen führt.

Wir können den Setter einfach so aufrufen

x = 10

Getter wird beim Abfeuern unterhalb der angegebenen Codezeile aufgerufen

var newVar = x
Sebin Roy
quelle
8

Sie definieren rekursivx mit x. Als ob dich jemand fragt, wie alt du bist? Und du antwortest "Ich bin doppelt so alt wie ich". Welches ist bedeutungslos.

Sie müssen sagen, ich bin doppelt so alt wie John oder eine andere Variable als Sie.

Berechnete Variablen sind immer von einer anderen Variablen abhängig.


Die Faustregel lautet, niemals von innen auf die Eigenschaft selbst zuzugreifen der Getter dh get. Denn das würde einen anderen getauslösen, der einen anderen auslösen würde. . . Drucken Sie es nicht einmal aus. Weil beim Drucken auch der Wert "abgerufen" werden muss, bevor er gedruckt werden kann!

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

let p1 = Person()

Da würde die folgende Warnung geben:

Der Versuch, von seinem eigenen Getter aus auf 'name' zuzugreifen.

Der Fehler sieht vage aus:

Geben Sie hier die Bildbeschreibung ein

Alternativ können Sie auch verwenden didSet. Mit erhalten didSetSie einen Halt für den Wert, der zuvor eingestellt wurde und auf den gerade eingestellt wurde. Weitere Informationen finden Sie in dieser Antwort .

Honig
quelle
8

Update: Swift 4

In der folgenden Klasse werden Setter und Getter auf Variablen angewendet 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
        }
   }

Objekt erstellen

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

Getter

print(triangle.perimeter) // invoking getter

Setter

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

Versuchen Sie Folgendes:

var x:Int!

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

Dies ist im Grunde die Antwort von Jack Wu, aber der Unterschied besteht darin, dass in Jack Wus Antwort seine x-Variable lautet var x: Int, in meiner ist meine x-Variable wie folgt: var x: Int!Also habe ich sie nur zu einem optionalen Typ gemacht.

Epischer Defeater
quelle
1

Update für Swift 5.1

Ab Swift 5.1 können Sie Ihre Variable jetzt ohne das Schlüsselwort get abrufen. Beispielsweise:

var helloWorld: String {
"Hello World"
}
atalayasa
quelle
Wenn ich versuche mich zu ändern, ist helloWorld = "macWorld" , kann dem Wert nicht zugewiesen werden: 'helloWorld' ist ein Nur-Get-Eigenschaftsfehler, der angezeigt wird.
McDonal_11
Ist es möglich, neue Werte zuzuweisen? oder Nein ?
McDonal_11
Ich denke nicht, dass es möglich ist.
Atalayasa
Also buchstäblich var helloWorld: String {"Hello World"} und let helloWorld: String = "Hello World" sind gleich?
McDonal_11
Ja, ich denke schon.
Atalayasa
0

Setter und Getter in Swift gelten für berechnete Eigenschaften / Variablen. Diese Eigenschaften / Variablen werden nicht tatsächlich im Speicher gespeichert, sondern basierend auf dem Wert der gespeicherten Eigenschaften / Variablen berechnet.

Weitere Informationen finden Sie in der Swift-Dokumentation von Apple zum Thema: Swift-Variablendeklarationen .

jefe2000
quelle
0

Hier ist eine theoretische Antwort. Das kann gefunden werden hier

Eine {get set} -Eigenschaft kann keine konstant gespeicherte Eigenschaft sein. Es sollte eine berechnete Eigenschaft sein und sowohl get als auch set sollten implementiert werden.

Talha Ahmad Khan
quelle