Pemrograman Swift: pengambil / penyetel dalam properti tersimpan

102

Bagaimana cara menimpa penyetel properti tersimpan di Swift?

Di Obj-C, saya bisa menimpa penyetelnya, tetapi Swift tampaknya tidak senang dengan pengambil / penyetel yang digunakan untuk properti tersimpan.

Katakanlah saya memiliki Cardkelas dengan properti bernama rank. Saya tidak ingin klien memberikan nilai yang tidak valid, oleh karena itu, dalam objektif-C, saya dapat menimpa setRanksehingga ia melakukan pemeriksaan tambahan. Tetapi willSetdi Swift sepertinya tidak membantu karena newValuekonstan dan tidak masuk akal untuk menetapkan rankkarena penyetel akan dipanggil dalam satu lingkaran.

bohanl
sumber
Sudahkah Anda menemukan cara untuk melakukan ini? Saya sendiri membutuhkan fungsi semacam ini ...
Mihai Fratu
Aku menemukannya. Lihat jawaban saya ...
Mihai Fratu
Bagaimana dengan didGet atau analog?
fnc12

Jawaban:

107

Baik. Membaca dokumentasi Apel di Swift saya menemukan ini :

Jika Anda menetapkan nilai ke properti dalam pengamat didSetnya sendiri, nilai baru yang Anda tetapkan akan menggantikan nilai yang baru saja ditetapkan.

Jadi yang harus Anda lakukan adalah ini:

var rank: Int = 0 {
    didSet {
        // Say 1000 is not good for you and 999 is the maximum you want to be stored there
        if rank >= 1000  {
            rank = 999
        }
    }
}
Mihai Fratu
sumber
Bagaimana dengan didGet atau analog?
fnc12
Tidak yakin saya mengerti pertanyaan Anda. Bisakah Anda lebih spesifik?
Mihai Fratu
Saya perlu meminta beberapa kode sebelum mendapatkannya. Saya dapat melakukan ini di obj-c tetapi saya tidak dapat melihat bagaimana melakukan ini di Swift. Satu-satunya hal yang saya lihat adalah menggunakan dua properti: satu publik dan satu lagi privat, publik memanggil kode saya dan mengembalikan nilai properti privat. Itulah mengapa saya bertanya tentang didGet
fnc12
Anda hanya dapat memiliki getter untuk properti yang dihitung. Misalnyavar rankTimesTwo: Int { get { return rank * 2 } }
Mihai Fratu
2
Bekerja seperti pesona! Berhati-hatilah, ini tidak akan dipanggil saat menyetel properti di init ()
Christoph
35

Anda tidak dapat mengganti get/ setuntuk properti tersimpan tetapi Anda dapat menggunakan pengamat properti willSet/ didSet:

var totalSteps: Int = 0 {
    willSet(newTotalSteps) {
        println("About to set totalSteps to \(newTotalSteps)")
    }
    didSet {
        if totalSteps > oldValue  {
            println("Added \(totalSteps - oldValue) steps")
        }
    }
}

Nama parameter default adalah newValueuntuk willSetdan oldValueuntuk didSet, atau Anda dapat menamainya sendiri seperti di willSet(newTotalSteps).

Joseph Mark
sumber
Ini bekerja. Tetapi tidak menyelesaikan masalah saya, mungkin saya tidak cukup jelas dalam pertanyaan awal saya.
Bohan
Katakanlah saya memiliki Cardkelas dengan properti bernama rank. Saya tidak ingin klien memberikan nilai apa pun, oleh karena itu, dalam tujuan-C, saya dapat menimpa setRanksehingga ia melakukan pemeriksaan tambahan. Tetapi willSetdi Swift sepertinya tidak membantu karena newValuekonstan dan tidak masuk akal untuk menetapkan rankkarena penyetel akan dipanggil dalam satu lingkaran.
Bohan
2
Saya tidak sepenuhnya yakin apa yang Anda maksud tetapi tidak dapatkah Anda gunakan didSetuntuk memeriksa ranksetelah disetel dan jika validasi gagal, setel ulang ke yang lain misalnya oldValue?
Joseph Mark
9

get dan set adalah untuk properti yang dihitung (mereka tidak memiliki penyimpanan pendukung). (Menurut pendapat saya, kata kunci 'var' membingungkan di sini)

  • willSet dan didSet dipanggil untuk variabel instance (Gunakan didSet untuk menimpa perubahan apa pun)
  • set dan get murni untuk properti yang dihitung
pengguna3675131
sumber
9

Jika Anda tidak ingin menggunakan didSet, yang memiliki masalah bahwa nilai properti untuk sementara salah, Anda harus menggabungkan properti yang dihitung di sekitarnya.

private var _foo:Int = 0
var foo:Int {
    get {
        return _foo
    }
    set {
        if(newValue > 999) {
            _foo = 999
        } else {
            _foo = newValue
        }
    }
}

Atau:

private var _foo:Int = 0
var foo:Int {
    get {
        return _foo
    }
    set {
        guard newValue <= 999 else {
            _foo = 999
            return
        }
        _foo = newValue
    }
}
Jim Driscoll
sumber
Tidak masuk akal menggunakan dua variabel.
Piyush
1
Ini hanya menggunakan satu properti (variabel): foohanyalah ekspresi yang dihitung dari _foo, jangan biarkan kata kunci "var" menipu Anda! Ini berarti bahwa ada dua entri yang dapat diakses dari namespace privat, tetapi itu tidak ada hubungannya dengan protected / publik dan menjaga nilainya tetap foovalid setiap saat. Ini pada dasarnya adalah pola "tampilan". Masalah yang Anda dapatkan dengan menulis ulang via didSet, selain itu memiliki periode tidak valid, adalah bahwa ada potensi signifikan untuk loop tak terbatas karena Anda memasukkan kembali didSethandler dari dalam didSet.
Jim Driscoll
-8

Contoh yang Disederhanakan:

class Shape {
    var sideLength: Double {
    get {
        return self.sideLength
    }
    set {
        // Implement the setter here.
        self.sideLength = newValue
    }
    }
}

Contoh lengkapnya

Lihat perimetercontoh ini.

Kutipan Dari: Apple Inc. "The Swift Programming Language." iBooks. https://itun.es/us/jEUH0.l

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
    get {
        return 3.0 * sideLength
    }
    set {
        sideLength = newValue / 3.0
    }
    }

    override func simpleDescription() -> String {
        return "An equilateral triagle with sides of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength”
Mike Rapadas
sumber
3
dalam hal ini, perimetermasih merupakan properti yang dihitung. Bagaimana cara menimpa sideLength tanpa memasukkan properti yang dihitung?
bohanl
@bohanl Saya telah menambahkan contoh yang disederhanakan menggunakan getdanset
Mike Rapadas
6
"Contoh lengkap" Anda menunjukkan properti yang dihitung, bukan properti yang disimpan, dan "contoh yang disederhanakan" Anda tidak berfungsi.
Caleb
Saya berdiri dikoreksi. Seolah-olah abstraksi @property (getter + setter yang disintesis otomatis) di Objective-C telah dihapuskan di Swift. Ironisnya ...
Mike Rapadas
6
Anda "contoh yang disederhanakan" memanggil pengambil di dalam pengambil itu sendiri. Inf loop ... crash.
jakenberg