Swift Programming: getter / setter ในคุณสมบัติที่เก็บไว้


102

ฉันจะเขียนทับ setter ของคุณสมบัติที่เก็บไว้ใน Swift ได้อย่างไร

ใน Obj-C ฉันสามารถเขียนทับ setter ได้ แต่ Swift ดูเหมือนจะไม่ค่อยพอใจกับการใช้ getter / setters สำหรับคุณสมบัติที่เก็บไว้

บอกว่าฉันมีระดับด้วยคุณสมบัติที่เรียกว่าCard rankฉันไม่ต้องการให้ไคลเอ็นต์ระบุค่าที่ไม่ถูกต้องดังนั้นในวัตถุประสงค์ -C ฉันสามารถเขียนทับsetRankเพื่อให้ทำการตรวจสอบเพิ่มเติม แต่willSetใน Swift ดูเหมือนจะไม่ช่วยเพราะnewValueมีค่าคงที่และไม่มีเหตุผลที่จะกำหนดrankเพราะ setter จะถูกเรียกแบบวนซ้ำ


คุณพบวิธีการทำเช่นนี้หรือไม่? ฉันต้องการฟังก์ชันแบบนี้ด้วยตัวเอง ...
Mihai Fratu

เจอแล้ว. ตรวจสอบคำตอบของฉัน ...
Mihai Fratu

แล้ว didGet หรืออะนาล็อกล่ะ?
fnc12

คำตอบ:


107

ตกลง. การอ่านเอกสารของ Apples บน Swift ฉันพบสิ่งนี้ :

หากคุณกำหนดค่าให้กับคุณสมบัติภายในผู้สังเกตการณ์ didSet ของตัวเองค่าใหม่ที่คุณกำหนดจะแทนที่ค่าที่เพิ่งตั้งค่า

สิ่งที่คุณต้องทำก็คือ:

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

แล้ว didGet หรืออะนาล็อกล่ะ?
fnc12

ไม่แน่ใจว่าฉันเข้าใจคำถามของคุณ คุณสามารถเจาะจงมากขึ้นได้ไหม
Mihai Fratu

ฉันต้องเรียกใช้รหัสก่อนที่จะรับ ฉันสามารถทำสิ่งนี้ได้ใน obj-c แต่ฉันไม่เห็นวิธีดำเนินการนี้ใน Swift สิ่งเดียวที่ฉันเห็นคือการใช้สองคุณสมบัติ: หนึ่งคือสาธารณะและหนึ่งเป็นแบบส่วนตัวสาธารณะเรียกใช้รหัสของฉันและคืนค่าทรัพย์สินส่วนตัว นั่นคือเหตุผลที่ฉันถามถึง didGet
fnc12

คุณสามารถมีได้เฉพาะคุณสมบัติที่คำนวณได้ ตัวอย่างเช่นvar rankTimesTwo: Int { get { return rank * 2 } }
Mihai Fratu

2
ใช้งานได้เหมือนมีเสน่ห์! ระวังจะไม่ถูกเรียกเมื่อตั้งค่าคุณสมบัติใน init ()
Christoph

35

คุณไม่สามารถแทนที่get/ setสำหรับคุณสมบัติที่เก็บไว้ แต่คุณสามารถใช้ผู้สังเกตการณ์คุณสมบัติwillSet/ didSet:

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

ชื่อพารามิเตอร์ดีฟอลต์มีnewValueไว้สำหรับwillSetและoldValueสำหรับdidSetหรือคุณสามารถตั้งชื่อเองwillSet(newTotalSteps)ได้


นี้ได้ผล แต่ไม่สามารถแก้ปัญหาของฉันได้บางทีฉันอาจจะไม่ชัดเจนพอในคำถามเดิมของฉัน
bohanl

บอกว่าฉันมีระดับด้วยคุณสมบัติที่เรียกว่าCard rankฉันไม่ต้องการให้ไคลเอ็นต์ให้ค่าใด ๆ ดังนั้นในวัตถุประสงค์ -C ฉันสามารถเขียนทับsetRankเพื่อให้ทำการตรวจสอบเพิ่มเติม แต่willSetใน Swift ดูเหมือนจะไม่ช่วยเพราะnewValueมีค่าคงที่และไม่มีเหตุผลที่จะกำหนดrankเพราะ setter จะถูกเรียกแบบวนซ้ำ
bohanl

2
ฉันไม่แน่ใจว่าคุณหมายถึงอะไร แต่คุณไม่สามารถใช้didSetเพื่อตรวจสอบrankหลังจากได้รับการตั้งค่าและหากไม่ผ่านการตรวจสอบความถูกต้องให้รีเซ็ตเป็นอย่างอื่นเช่นoldValue?
Joseph Mark

9

รับและตั้งค่าสำหรับคุณสมบัติที่คำนวณได้ (ไม่มีที่เก็บสำรอง) (ในความคิดของฉันคำหลัก 'var' สับสนที่นี่)

  • willSet และ didSet ถูกเรียกสำหรับตัวแปรอินสแตนซ์ (ใช้ didSet เพื่อแทนที่การเปลี่ยนแปลงใด ๆ )
  • set and get ใช้สำหรับคุณสมบัติที่คำนวณเท่านั้น

9

หากคุณไม่ต้องการใช้ didSet ซึ่งมีปัญหาว่าค่าของคุณสมบัติไม่ถูกต้องชั่วคราวคุณควรรวมคุณสมบัติที่คำนวณไว้รอบ ๆ

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

หรือ:

private var _foo:Int = 0
var foo:Int {
    get {
        return _foo
    }
    set {
        guard newValue <= 999 else {
            _foo = 999
            return
        }
        _foo = newValue
    }
}

มันไม่สมเหตุสมผลที่จะใช้สองตัวแปร
Piyush

1
นี่ใช้เพียงคุณสมบัติเดียว (ตัวแปร): fooเป็นเพียงนิพจน์ที่คำนวณได้ของ_fooอย่าปล่อยให้คีย์เวิร์ด "var" หลอกคุณ! หมายความว่ามีสองรายการที่สามารถเข้าถึงได้จากเนมสเปซส่วนตัว แต่ไม่มีผลต่อการป้องกัน / สาธารณะและรักษาค่าที่fooถูกต้องตลอดเวลา นี่คือรูปแบบ "มุมมอง" เป็นหลัก ปัญหาที่คุณได้รับกับการเขียนใหม่ผ่านdidSetในนอกจากนั้นมีระยะเวลาของการเป็นที่ไม่ถูกต้องคือการที่มีศักยภาพที่สำคัญสำหรับวง จำกัด เนื่องจากคุณต้องป้อนจัดการจากภายในdidSet didSet
Jim Driscoll

-8

ตัวอย่างง่าย:

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

ตัวอย่างเต็ม

ลองดูperimeterในตัวอย่างนี้

ตัดตอนมาจาก: 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”

3
ในกรณีperimeterนี้ยังคงเป็นคุณสมบัติที่คำนวณได้ ฉันจะเขียนทับ sideLength โดยไม่ต้องใช้คุณสมบัติที่คำนวณได้อย่างไร
bohanl

@bohanl ฉันได้เพิ่มตัวอย่างที่เรียบง่ายโดยใช้getและset
Mike Rapadas

6
"ตัวอย่างเต็ม" ของคุณแสดงพร็อพเพอร์ตี้ที่คำนวณไม่ใช่คุณสมบัติที่จัดเก็บและ "ตัวอย่างแบบง่าย" ของคุณไม่ทำงาน
Caleb

ฉันยืนแก้ไข ราวกับว่านามธรรมของ @property (getters สังเคราะห์อัตโนมัติ + ตัวตั้งค่า) ใน Objective-C นั้นไม่ได้ถูกทำให้เป็นนามธรรมใน Swift ประชด ...
Mike Rapadas

6
คุณกำลัง "ตัวอย่างที่เรียบง่าย" กำลังเรียกใช้ getter ภายใน getter ของตัวเอง Inf loop ... ผิดพลาด
jakenberg
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.