Геттеры и сеттеры в Swift. Имеет ли смысл вместо этого использовать WillSet и DidSet?

Я занимался исследованием причин, по которым мы должны использовать Get и Set для наших свойств.

Я заметил 3 основные причины этого

  1. Когда вы хотите что-то сделать/проверить, прежде чем установить свойство
  2. Когда вы хотите иметь свойство, которое вы можете получить только от него (может быть, в целях безопасности, я думаю?), или предоставить ему разные уровни доступа.
  3. Скрытие внутреннего представления свойства при отображении свойства с использованием альтернативного представления. (что для меня не имеет большого смысла, так как я все равно могу получить к нему доступ не в том месте, используя функцию Set)

Приведенный ниже код является примером того, как вы могли бы реализовать Get и Set для свойств в Swift, используя в своих интересах те 3 пункта, которые я упомянул:

class Test
{
    private var _testSet:String!
    private var _testGetOnly:String
    var testSet:String{
        get{
            return _testSet
        }
        set{
            _testSet = newValue + "you forgot this string"
        }
    }
    var testGetOnly:String!{
        get{
            return _testGetOnly
        }
    }

    init(testSet:String, testGetOnly:String)
    {
        _testSet = testSet
        _testGetOnly = testGetOnly
    }
}

Но в этом другом примере ниже также используются упомянутые моменты, но вместо использования другого вычисляемого свойства для возврата значения частного свойства я просто использую наблюдатели willSet и didSet.

class Test
{
    var testGet:String {
        willSet{
            fatalError("Operation not allowed")
        }
    }
    var testWillSet:String!{
        didSet{
            self.testWillSet = self.testWillSet + "you forgot this string"
        }
    }
    init(testGet:String, testWillSet:String)
    {
        self.testGet = testGet
        self.testWillSet = testWillSet 
    }
}

Поэтому мне любопытно узнать, каковы ПРЕИМУЩЕСТВА и НЕДОСТАТКИ каждой реализации.

заранее спасибо


person Henrique da Costa    schedule 31.08.2016    source источник
comment
Очень интересный вопрос, но я не могу вам помочь, так как моб был обнаружен...   -  person Felipe Ribeiro R. Magalhaes    schedule 31.08.2016
comment
willSet и didSet являются наблюдателями. Они полностью отличаются от геттеров/сеттеров. (Даже не упоминайте, что часто свойства в Swift (например, в расширении протокола) даже не свойства, а вычисляемые свойства.) Отвечая на ваш вопрос, да, во что бы то ни стало, два наблюдателя - такие замечательные языковые функции. , что очень часто вам просто не нужно больше беспокоиться об использовании утомительного шаблона свойство/геттер/сеттер - отлично.   -  person Fattie    schedule 01.02.2017


Ответы (1)


Ваш вопрос сводится к ошибке времени компиляции и времени выполнения. Чтобы ответить на ваши 3 вопроса:

  1. Да, willCheck – ваш единственный вариант.
  2. Свойства только для чтения делятся на 2 типа: (а) те, значение которых выводится из других свойств, например, их суммы; и (б) те, которые вы хотите изменить самостоятельно, но не пользователями. Первый тип действительно не имеет сеттера; второй тип имеет общедоступный геттер и частный сеттер. Компилятор может помочь вам проверить это, и программа не будет компилироваться. Если вы добавите fatalError в didSet, вы получите ошибку времени выполнения, и ваше приложение выйдет из строя.
  3. Могут быть объекты состояния, с которыми вы не хотите, чтобы пользователь свободно возился, и да, вы можете полностью скрыть их от пользователей.

Первый пример вашего кода был слишком подробным при определении вспомогательных переменных - вам не нужно этого делать. Чтобы проиллюстрировать эти моменты:

class Test
{
    // 1. Validate the new value
    var mustBeginWithA: String = "A word" {
        willSet {
            if !newValue.hasPrefix("A") {
                fatalError("This property must begin with the letter A")
            }
        }
    }

    // 2. A readonly property
    var x: Int = 1
    var y: Int = 2
    var total: Int {
        get { return x + y }
    }

    private(set) var greeting: String = "Hello world"
    func changeGreeting() {
        self.greeting = "Goodbye world" // Even for private property, you may still
                                        // want to set it, just not allowing the user
                                        // to do so
    }

    // 3. Hide implementation detail
    private var person = ["firstName": "", "lastName": ""]
    var firstName: String {
        get { return person["firstName"]! }
        set { person["firstName"] = newValue }
    }

    var lastName: String {
        get { return person["lastName"]! }
        set { person["lastName"] = newValue }
    }

    var fullName: String {
        get { return self.firstName + " " + self.lastName }
        set {
            let components = newValue.componentsSeparatedByString(" ")
            self.firstName = components[0]
            self.lastName = components[1]
        }
    }
}

Использование:

let t = Test()
t.mustBeginWithA = "Bee"        // runtime error

t.total = 30                    // Won't compile

t.greeting = "Goodbye world"    // Won't compile. The compiler does the check for you
                                // instead of a crash at run time

t.changeGreeting()              // OK, greeting now changed to "Goodbye world"

t.firstName = "John"            // Users have no idea that they are actually changing 
t.lastName = "Smith"            // a key in the dictionary and there's no way for them
                                // to access that dictionary

t.fullName = "Bart Simpsons"    // You do not want the user to change the full name
                                // without making a corresponding change in the
                                // firstName and lastName. With a custome setter, you
                                // can update both firstName and lastName to maintain
                                // consistency

Примечание о private в Swift 2 и Swift 3: если вы попробуете это на игровой площадке Swift 2, вы обнаружите, что t.greeting = "Goodbye world" работает очень хорошо. Это связано с тем, что Swift 2 имеет странный спецификатор уровня доступа: private означает «доступен только в текущем файле». Разделите определение класса и пример кода на разные файлы, и Xcode будет жаловаться. В Swift 3 это было изменено на fileprivate, что более понятно и сохраняет ключевое слово private для чего-то более похожего на Java и .NET.

person Code Different    schedule 01.09.2016