Getter e setter di proprietà


194

Con questa semplice classe ricevo l' avviso del compilatore

Tentativo di modifica / accesso xall'interno del proprio setter / getter

e quando lo uso in questo modo:

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

Ricevo un EXC_BAD_ACCESS. Come posso farlo senza esplicito supporto ivar?

class Point {

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

1
Ciò comporterebbe anche un carico aggiuntivo sul compilatore e consumerebbe energicamente la CPU. L'ho appena fatto: | . Questo è stato l'errore che stavo creando. (Stavo usando il parco giochi)
Honey,

Questo comportamento è confuso, invece di usare quello setche vuoi usare didSet. Le proprietà si comportano diversamente in Swift rispetto a Objective-C o in altre lingue durante l'implementazione set. Vedi la risposta di seguito da @jack e un esempio di didSetda @cSquirrel
Paul Solt

Risposte:


232

Setters e Getters si applicano a computed properties; tali proprietà non dispongono di memoria nell'istanza: il valore del getter deve essere calcolato da altre proprietà dell'istanza. Nel tuo caso, non c'è xda assegnare.

Esplicitamente: "Come posso fare questo senza esplicito supporto ivar". Non puoi - avrai bisogno di qualcosa per il backup della proprietà calcolata. Prova questo:

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

Nello specifico, nel REPL di Swift:

 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

I setter / getter in Swift sono abbastanza diversi da ObjC. La proprietà diventa una proprietà calcolata, il che significa che non ha una variabile di supporto come_x come in ObjC.

Nel codice della soluzione di seguito puoi vedere xTimesTwoche non memorizza nulla, ma semplicemente calcola il risultatox .

Vedi Documenti ufficiali sulle proprietà calcolate .

La funzionalità desiderata potrebbe anche essere Osservatori di proprietà .

Ciò di cui hai bisogno è:

var x: Int

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

Puoi modificare altre proprietà all'interno del setter / getter, che è ciò per cui sono destinate.


1
Sì, è quello che sto leggendo nella documentazione. Ho saltato la sezione delle proprietà e sembra che non ci sia modo di aggirare questo, giusto?
Atomix,

Sì, avrai bisogno di un'altra variabile di istanza per "indietro" la prima se vuoi davvero farlo. Tuttavia, i setter non sono pensati per questo scopo, probabilmente dovresti ripensare a ciò che stai cercando di ottenere.
Jack

5
è possibile utilizzare didSetciò che consente di modificare il valore immediatamente dopo averlo impostato. Niente da fare però ...
ObjectiveCsam

1
NOTA: se si desidera ripristinare il valore perché è stato un input non valido, è necessario impostare xnuovamente su oldValuein didSet. Questo cambiamento nel comportamento è molto confuso derivante dalle proprietà Objective-C, grazie!
Paul Solt,

105

È possibile personalizzare il valore impostato utilizzando l'osservatore proprietà. Per fare questo usa 'didSet' invece di 'set'.

class Point {

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

Per quanto riguarda getter ...

class Point {

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

Come è possibile inizializzare xcon un valore predefinito in questo modello?
i_am_jorf,

3
Vedo, sarebbe: var x: Int = 0 { didSet { ....
i_am_jorf,

31

Per approfondire la risposta di GoZoner:

Il tuo vero problema qui è che stai chiamando ricorsivamente il tuo 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
        }
    }

Come suggerisce il commento del codice sopra, stai chiamando all'infinito il getter della proprietà x, che continuerà ad essere eseguito fino a quando non ottieni un codice EXC_BAD_ACCESS (puoi vedere lo spinner nell'angolo in basso a destra dell'ambiente di gioco del tuo Xcode).

Considera l'esempio della documentazione di 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)
        }
    }
}

Notare come la proprietà calcolata centrale non modifica o restituisce mai se stessa nella dichiarazione della variabile.


La regola del pollice non è mai accedere alla proprietà stessa dall'interno del getter get. Perché ciò innescerebbe un altro getche innescerebbe un altro. . . Non stamparlo nemmeno. Perché la stampa richiede anche di "ottenere" il valore prima di poterlo stampare!
Miele

19

Per sovrascrivere settere getterper le variabili rapide utilizzare il codice indicato di seguito

var temX : Int? 
var x: Int?{

    set(newX){
       temX = newX
    }

    get{
        return temX
    }
}

Dobbiamo mantenere il valore della variabile in una variabile temporanea, poiché il tentativo di accedere alla stessa variabile il cui getter / setter viene sovrascritto comporterà cicli infiniti.

Possiamo invocare il setter in questo modo

x = 10

Getter verrà invocato al momento dello sparo sotto la riga di codice indicata

var newVar = x

8

Stai definendo ricorsivamentex conx . Come se qualcuno ti chiedesse quanti anni hai? E tu rispondi "Ho il doppio della mia età". Che è insignificante.

Devi dire che ho il doppio dell'età di John o qualsiasi altra variabile ma te stesso.

le variabili calcolate dipendono sempre da un'altra variabile.


La regola del pollice non è mai accedere alla proprietà stessa dall'interno del getter get. Perché ciò innescerebbe un altro getche innescerebbe un altro. . . Non stamparlo nemmeno. Perché la stampa richiede anche di "ottenere" il valore prima di poterlo stampare!

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

let p1 = Person()

Poiché ciò darebbe il seguente avvertimento:

Tentativo di accedere a "nome" dall'interno del proprio getter.

L'errore appare vago in questo modo:

inserisci qui la descrizione dell'immagine

In alternativa potresti voler usare didSet. Con didSetotterrai un valore che è stato impostato in precedenza e appena impostato. Per di più vedi questa risposta .


8

Aggiornamento: Swift 4

Nella classe sottostante setter e getter vengono applicati alla variabile 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
        }
   }

Creare un oggetto

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

procacciatore

print(triangle.perimeter) // invoking getter

Setter

triangle.perimeter = 9.9 // invoking setter

6

Prova a usare questo:

var x:Int!

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

Questa è sostanzialmente la risposta di Jack Wu, ma la differenza è che nella risposta di Jack Wu la sua variabile x è var x: Int, nella mia, la mia variabile x è così:, var x: Int!quindi tutto quello che ho fatto è stato renderlo un tipo opzionale.


1

Aggiornamento per Swift 5.1

A partire da Swift 5.1 ora puoi ottenere la tua variabile senza usare la parola chiave get . Per esempio:

var helloWorld: String {
"Hello World"
}

Se provo a cambiare, helloWorld = "macWorld" , Impossibile assegnare al valore: "helloWorld" è un errore di proprietà di sola acquisizione.
McDonal_11,

è possibile assegnare nuovi valori? o no ?
McDonal_11,

Non penso sia possibile.
Atalayasa,

Quindi, letteralmente, var helloWorld: String {"Hello World"} e, lascia che helloWorld: String = "Hello World" siano uguali?
McDonal_11,

Penso di sì.
Atalayasa,

0

I setter e i getter in Swift si applicano alle proprietà / variabili calcolate. Queste proprietà / variabili non sono effettivamente memorizzate nella memoria, ma piuttosto calcolate in base al valore delle proprietà / variabili memorizzate.

Consulta la documentazione di Apple Swift sull'argomento: Swift Variable Declarations .


0

Ecco una risposta teorica. Questo può essere trovato qui

Una proprietà {get set} non può essere una proprietà memorizzata costante. Dovrebbe essere una proprietà calcolata e sia get che set dovrebbero essere implementati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.