Pengambil dan penyetor properti

194

Dengan kelas sederhana ini saya mendapatkan peringatan compiler

Mencoba untuk memodifikasi / mengakses xdalam setter / pengambil sendiri

dan ketika saya menggunakannya seperti ini:

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

Saya mendapatkan EXC_BAD_ACCESS. Bagaimana saya bisa melakukan ini tanpa dukungan vars secara eksplisit?

class Point {

    var x: Int {
        set {
            x = newValue * 2 //Error
        }
        get {
            return x / 2 //Error
        }
    }
    // ...
}
Atomix
sumber
1
Melakukan hal itu juga akan menambah beban pada kompiler dan akan mengkonsumsi CPU dengan giat. Saya baru saja melakukannya: | . Ini adalah kesalahan yang saya buat. (Saya menggunakan taman bermain)
Sayang
Perilaku ini membingungkan, alih-alih menggunakan yang setAnda ingin gunakan didSet. Properti berperilaku berbeda di Swift daripada Objective-C atau bahasa lain ketika Anda menerapkan set. Lihat jawaban di bawah ini dari @jack dan contoh didSetdari @cSquirrel
Paul Solt

Jawaban:

232

Setter dan Getters berlaku untuk computed properties; properti seperti itu tidak memiliki penyimpanan dalam instance - nilai dari pengambil dimaksudkan untuk dihitung dari properti instance lainnya. Dalam kasus Anda, tidak ada yang xditugaskan.

Secara eksplisit: "Bagaimana saya bisa melakukan ini tanpa dukungan ivar secara eksplisit". Anda tidak bisa - Anda akan membutuhkan sesuatu untuk membuat cadangan properti yang dihitung. Coba ini:

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

Secara khusus, dalam REPL 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
GoZoner
sumber
106

Setter / getter di Swift sangat berbeda dari ObjC. Properti menjadi properti yang dihitung yang artinya tidak memiliki variabel pendukung seperti yang _xakan terjadi pada ObjC.

Dalam kode solusi di bawah ini Anda dapat melihat xTimesTwotidak tidak menyimpan apa-apa, tetapi hanya menghitung hasil dari x.

Lihat dokumen resmi tentang properti yang dihitung .

Fungsi yang Anda inginkan mungkin juga Pengamat Properti .

Yang Anda butuhkan adalah:

var x: Int

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

Anda dapat memodifikasi properti lain dalam setter / getter, yang memang dimaksudkan untuk itu.

Mendongkrak
sumber
1
Ya, itulah yang saya baca di dokumentasi. Saya melewatkan bagian properti dan sepertinya tidak ada jalan lain, kan?
Atomix
Ya, Anda akan membutuhkan variabel instance lain untuk "kembali" yang pertama jika Anda benar-benar ingin melakukan ini. Namun, setter tidak dimaksudkan untuk tujuan ini, Anda mungkin harus memikirkan kembali apa yang ingin Anda capai dengan ini.
Jack
5
Anda bisa menggunakan didSetyang akan memungkinkan Anda untuk mengubah nilai segera setelah ditetapkan. Tidak ada yang bisa didapat ...
ObjectiveCsam
1
CATATAN: Jika Anda ingin mengembalikan nilai karena input yang tidak valid, Anda perlu mengatur xkembali ke oldValuedalam didSet. Perubahan perilaku ini sangat membingungkan karena berasal dari properti Objective-C, terima kasih!
Paul Solt
105

Anda dapat menyesuaikan nilai yang ditetapkan menggunakan pengamat properti. Untuk melakukan ini gunakan 'didSet' bukan 'set'.

class Point {

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

Adapun pengambil ...

class Point {

var doubleX: Int {
    get {
        return x / 2
    }
}
...
cSquirrel
sumber
Bagaimana Anda bisa menginisialisasi xdengan nilai default dalam pola ini?
i_am_jorf
3
Saya melihat, itu akan menjadi: var x: Int = 0 { didSet { ....
i_am_jorf
31

Untuk menguraikan jawaban GoZoner:

Masalah sebenarnya Anda di sini adalah bahwa Anda secara rekursif memanggil pengambil Anda.

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

Seperti yang dikomentari oleh kode di atas, Anda memanggil pengambil properti x, yang akan terus dijalankan hingga Anda mendapatkan kode EXC_BAD_ACCESS (Anda dapat melihat pemintal di sudut kanan bawah lingkungan taman bermain Xcode Anda).

Pertimbangkan contoh dari dokumentasi 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)
        }
    }
}

Perhatikan bagaimana pusat menghitung properti tidak pernah memodifikasi atau mengembalikan dirinya sendiri dalam deklarasi variabel.

Abdullah Bakhsh
sumber
Aturan praktis adalah tidak pernah mengakses properti itu sendiri dari dalam pengambil yaitu get. Karena itu akan memicu yang lain getyang akan memicu yang lain. . . Jangan pernah mencetaknya. Karena pencetakan juga mengharuskan untuk mendapatkan nilai sebelum dapat mencetaknya!
Sayang
19

Untuk mengganti setterdan getteruntuk variabel cepat gunakan kode yang diberikan di bawah ini

var temX : Int? 
var x: Int?{

    set(newX){
       temX = newX
    }

    get{
        return temX
    }
}

Kita perlu menyimpan nilai variabel dalam variabel sementara, karena mencoba mengakses variabel yang sama yang pengambil / penyetelnya ditimpa akan menghasilkan loop tak terbatas.

Kita bisa meminta setter seperti ini

x = 10

Getter akan dipanggil pada pembakaran di bawah baris kode yang diberikan

var newVar = x
Sebin Roy
sumber
8

Anda rekursif mendefinisikan xdengan x. Seolah-olah seseorang bertanya berapa umurmu? Dan Anda menjawab "Saya dua kali usia saya". Itu tidak ada artinya.

Anda harus mengatakan usia saya dua kali John atau variabel lain selain diri Anda sendiri.

variabel yang dihitung selalu tergantung pada variabel lain.


Aturan praktis adalah tidak pernah mengakses properti itu sendiri dari dalam pengambil yaitu get. Karena itu akan memicu yang lain getyang akan memicu yang lain. . . Jangan pernah mencetaknya. Karena pencetakan juga mengharuskan untuk mendapatkan nilai sebelum dapat mencetaknya!

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

let p1 = Person()

Karena itu akan memberikan peringatan berikut:

Mencoba mengakses 'nama' dari dalam pembuatnya sendiri.

Kesalahannya terlihat tidak jelas seperti ini:

masukkan deskripsi gambar di sini

Sebagai alternatif, Anda mungkin ingin menggunakan didSet. Dengan didSetAnda akan mendapatkan nilai yang ditetapkan sebelumnya dan baru saja ditetapkan untuk. Untuk lebih lanjut lihat jawaban ini .

Madu
sumber
8

Pembaruan: Swift 4

Dalam setter dan pengambil kelas di bawah ini diterapkan untuk variabel 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
        }
   }

Menciptakan objek

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

Getter

print(triangle.perimeter) // invoking getter

Setter

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

Coba gunakan ini:

var x:Int!

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

Ini pada dasarnya adalah jawaban Jack Wu, tetapi perbedaannya adalah bahwa dalam jawaban Jack Wu variabel x-nya adalah var x: Int, pada variabel x saya, adalah seperti ini:, var x: Int!jadi yang saya lakukan hanyalah menjadikannya tipe opsional.

Defeater Epic
sumber
1

Pembaruan untuk Swift 5.1

Pada Swift 5.1 Anda sekarang bisa mendapatkan variabel Anda tanpa menggunakan kata kunci get . Sebagai contoh:

var helloWorld: String {
"Hello World"
}
atalayasa
sumber
Jika saya mencoba mengubah, helloWorld = "macWorld" , Tidak dapat menetapkan nilai: 'helloWorld' adalah kesalahan properti get-only yang ditampilkan.
McDonal_11
apakah mungkin untuk menetapkan nilai baru? atau tidak ?
McDonal_11
Saya pikir itu tidak mungkin.
atalayasa
Jadi secara literal, var helloWorld: String {"Hello World"} dan, biarkan helloWorld: String = "Hello World" sama?
McDonal_11
Ya saya pikir begitu.
atalayasa
0

Setter dan getter di Swift berlaku untuk properti / variabel yang dihitung. Properti / variabel ini sebenarnya tidak disimpan dalam memori, melainkan dihitung berdasarkan nilai properti / variabel yang disimpan.

Lihat dokumentasi Swift Apple tentang masalah ini: Deklarasi Variabel Swift .

jefe2000
sumber
0

Ini jawaban teoretisnya. Itu bisa ditemukan di sini

Properti {get set} tidak boleh berupa properti tersimpan konstan. Ini harus menjadi properti yang dihitung dan keduanya mendapatkan dan mengatur harus diimplementasikan.

Talha Ahmad Khan
sumber