Lập trình Swift: getter / setter trong thuộc tính được lưu trữ


102

Làm cách nào để ghi đè bộ thiết lập của thuộc tính được lưu trữ trong Swift?

Trong Obj-C, tôi có thể ghi đè setter của nó, nhưng Swift có vẻ không hài lòng về việc getter / setters được sử dụng cho thuộc tính được lưu trữ.

Giả sử tôi có một Cardlớp với thuộc tính được gọi rank. Tôi không muốn khách hàng cung cấp cho nó bất kỳ giá trị không hợp lệ nào, do đó, trong mục tiêu-C, tôi có thể ghi đè setRankđể nó thực hiện kiểm tra bổ sung. Nhưng willSettrong Swift dường như không giúp được gì vì newValuelà hằng số và không có ý nghĩa gì khi gán rankvì setter sẽ được gọi trong một vòng lặp.


Bạn đã tìm ra cách làm việc này chưa? Tôi cần loại chức năng bản thân mình ...
Mihai Fratu

Tôi đã tìm thấy nó. Kiểm tra câu trả lời của tôi ...
Mihai Fratu

Điều gì về didGet hoặc tương tự?
fnc12

Câu trả lời:


107

Đồng ý. Đọc qua tài liệu của Apples về Swift, tôi thấy điều này :

Nếu bạn gán giá trị cho một thuộc tính trong trình quan sát didSet của chính nó, giá trị mới mà bạn gán sẽ thay thế giá trị vừa được đặt.

Vì vậy, tất cả những gì bạn phải làm là:

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

Điều gì về didGet hoặc tương tự?
fnc12

Không chắc tôi hiểu câu hỏi của bạn. Bạn có thể cho biết cụ thể hơn được không?
Mihai Fratu

Tôi cần gọi một số mã trước khi nhận được. Tôi đã có thể thực hiện điều này trong obj-c nhưng tôi không thể biết cách thực hiện điều này trong Swift. Điều duy nhất tôi thấy là sử dụng hai thuộc tính: một là public và một là private, public gọi mã của tôi và trả về giá trị của private property. Đó là lý do tại sao tôi hỏi về didGet
fnc12

Bạn chỉ có thể có getter cho một thuộc tính được tính toán. Ví dụvar rankTimesTwo: Int { get { return rank * 2 } }
Mihai Fratu,

2
Hoạt động như một sự quyến rũ! Hãy lưu ý rằng nó sẽ không được gọi khi đặt thuộc tính trong init ()
Christoph

35

Bạn không thể ghi đè get/ setđối với một thuộc tính được lưu trữ nhưng bạn có thể sử dụng trình quan sát thuộc tính willSet/ didSet:

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

Các tên tham số mặc định là newValuefor willSetoldValuefor didSethoặc bạn có thể tự đặt tên cho chúng như trong willSet(newTotalSteps).


Những công việc này. Nhưng không giải quyết được vấn đề của tôi, có lẽ tôi đã không đủ rõ ràng trong câu hỏi ban đầu của mình.
bohanl

Giả sử tôi có một Cardlớp với thuộc tính được gọi rank. Tôi không muốn khách hàng cung cấp cho nó bất kỳ giá trị nào, do đó, trong mục tiêu-C, tôi có thể ghi đè setRankđể nó thực hiện kiểm tra bổ sung. Nhưng willSettrong Swift dường như không giúp được gì vì newValuelà hằng số và không có ý nghĩa gì khi gán rankvì setter sẽ được gọi trong một vòng lặp.
bohanl

2
Tôi không hoàn toàn chắc chắn ý bạn nhưng bạn không thể sử dụng didSetđể kiểm tra ranksau khi nó được thiết lập và nếu nó không được xác thực, hãy đặt lại nó thành một cái gì đó khác, ví dụ oldValue?
Joseph Mark

9

get và set dành cho các thuộc tính được tính toán (chúng không có bất kỳ kho dự trữ nào). (Theo tôi, từ khóa 'var' gây nhầm lẫn ở đây)

  • willSet và didSet được gọi cho một biến thể hiện (Sử dụng didSet để ghi đè bất kỳ thay đổi nào)
  • set và get hoàn toàn dành cho các thuộc tính được tính toán

9

Nếu bạn không muốn sử dụng didSet, có vấn đề là giá trị của thuộc tính tạm thời bị sai, bạn nên bọc một thuộc tính đã tính toán xung quanh nó.

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

Hoặc là:

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

Sẽ không hợp lý khi sử dụng hai biến.
Piyush

1
Điều này chỉ sử dụng một thuộc tính (biến): foochỉ là một biểu thức được tính toán của _foo, đừng để từ khóa "var" đánh lừa bạn! Điều đó có nghĩa là có hai mục nhập có thể truy cập từ không gian tên riêng, nhưng điều đó không liên quan đến bảo vệ / công khai và luôn giữ giá trị foohợp lệ. Đây thực chất là mẫu "view". Vấn đề bạn gặp phải với việc viết lại thông qua didSet, ngoài việc nó có một khoảng thời gian không hợp lệ, là có khả năng đáng kể cho một vòng lặp vô hạn vì bạn đang nhập lại didSettrình xử lý từ bên trong didSet.
Jim Driscoll

-8

Ví dụ đơn giản hóa:

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

Đầy đủ ví dụ

Kiểm tra perimetertrong ví dụ này.

Trích từ: Apple Inc. “Ngôn ngữ lập trình Swift.” 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
trong trường hợp này, perimetervẫn là một tài sản được tính toán. Làm cách nào để ghi đè sideLength mà không giới thiệu thuộc tính tính toán?
bohanl

@bohanl Tôi đã thêm một ví dụ đơn giản sử dụng getset
Mike Rapadas

6
"Ví dụ đầy đủ" của bạn hiển thị thuộc tính được tính toán, không phải thuộc tính được lưu trữ và "ví dụ đơn giản" của bạn không hoạt động.
Caleb

Tôi đứng sửa lại. Như thể sự trừu tượng của @property (getters + setters được tổng hợp tự động) trong Objective-C đã không được rút gọn trong Swift. Sự trớ trêu ...
Mike Rapadas

6
Bạn "ví dụ đơn giản" đang gọi một getter trong một getter của chính nó. Vòng lặp Inf ... sự cố.
jakenberg
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.