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.