Swift Programming: геттер / сеттер в сохраненном свойстве

102

Как мне перезаписать установщик сохраненного свойства в Swift?

В Obj-C я могу перезаписать его сеттер, но Swift, похоже, не доволен тем, что геттеры / сеттеры используются для сохраненного свойства.

Скажем, у меня есть Cardкласс со свойством под названием rank. Я не хочу, чтобы клиент давал ему какое-либо недопустимое значение, поэтому в объекте-C я могу перезаписать, setRankчтобы он выполнял дополнительную проверку. Но willSetв Swift, похоже, не помогает, потому что newValueон постоянен и нет смысла назначать, rankпотому что сеттер будет вызываться в цикле.

Bohanl
источник
Вы нашли способ сделать это? Мне самому нужна такая функциональность ...
Mihai Fratu
Я нашел это. Ознакомьтесь с моим ответом ...
Михай Фрату
А что с didGet или аналогом?
fnc12

Ответы:

107

Хорошо. Читая документацию Apple по 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
Не уверен, что понимаю ваш вопрос. Не могли бы вы уточнить, пожалуйста?
Михай Фрату
Мне нужно вызвать код перед тем, как получить. Мне удалось выполнить это в obj-c, но я не вижу, как это сделать в Swift. Единственное, что я вижу, - это использовать два свойства: одно публичное, а другое частное, публичное вызывает мой код и возвращает значение частного свойства. Вот почему я спросил про didGet
fnc12
У вас может быть только получатель для вычисляемого свойства. Напримерvar rankTimesTwo: Int { get { return rank * 2 } }
Михай Фрату
2
Работает как шарм! Остерегайтесь, он не будет вызываться при установке свойства в init ()
Кристоф
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. Я не хочу, чтобы клиент давал ему какое-либо значение, поэтому в objective-C я могу перезаписать, setRankчтобы он выполнил дополнительную проверку. Но willSetв Swift, похоже, не помогает, потому что newValueон постоянен и нет смысла назначать, rankпотому что сеттер будет вызываться в цикле.
bohanl
2
Я не совсем уверен, что вы имеете в виду, но не могли бы вы использовать didSetдля проверки rankпосле того, как он был установлен, и если он не прошел проверку, сбросьте его на что-то еще, например oldValue?
Джозеф Марк
9

get и set предназначены для вычисляемых свойств (у них нет резервного хранилища). (На мой взгляд, ключевое слово var здесь сбивает с толку)

  • willSet и didSet вызываются для переменной экземпляра (используйте didSet для отмены любых изменений)
  • set и get предназначены исключительно для вычисляемых свойств
user3675131
источник
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
    }
}
Джим Дрисколл
источник
Не имеет смысла использовать две переменные.
Пиюш
1
Здесь используется только одно свойство (переменная): fooэто просто вычисленное выражение _foo, не позволяйте ключевому слову "var" ввести вас в заблуждение! Это означает, что есть две записи, доступные из частного пространства имен, но это не имеет отношения к защищенному / общедоступному и сохраняет значение fooдействительного всегда. По сути, это паттерн «взгляд». Проблема, с которой вы сталкиваетесь при переписывании через didSet, помимо того, что он имеет период недействительности, заключается в том, что существует значительный потенциал для бесконечного цикла, поскольку вы повторно вводите didSetобработчик изнутри didSet.
Джим Дрисколл,
-8

Упрощенный пример:

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

Полный пример

Посмотрите perimeterв этом примере.

Отрывок из: Apple Inc. «Быстрый язык программирования». 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 09
@bohanl Я добавил упрощенный пример с использованием getиset
Майк Рападас
6
Ваш «полный пример» показывает вычисляемое свойство, а не сохраненное свойство, и ваш «упрощенный пример» не работает.
Калеб
Я исправился. Это как если бы абстракция @property (автоматически синтезируемые геттеры + сеттеры) в Objective-C не была абстракцией в Swift. Ирония судьбы ...
Майк Рападас 09
6
Ваш «упрощенный пример» вызывает геттер внутри самого геттера. Инф петля ... сбой.
jakenberg