Как я могу переопределить сеттер из SuperClass в Swift с Xcode 6.3 Beta2?

Мой SuerClass - UICollectionViewCell которого есть свойство:

var selected: Bool

Мой класс

MyClass : UICollectionViewCell {

  func setSelected(selected: Bool) { 
    super.selected = selected
    // do something
 }

}

Первый работал хорошо в Xcode 6.2, но в Xcode 6.3Beta2 возникает ошибка:

Method 'setSelected' with Objective-C selector 'setSelected:' conflicts with setter for 'selected' from superclass 'UICollectionViewCell' with the same Objective-C selector

Как я могу это исправить для работы с Xcode 6.3 beta2?

Изменение: я также пытался:

  override func setSelected(selected: Bool) { 
    super.selected = selected
    // do something
 }

Это приводит к ошибке:

Метод не переопределяет любой метод из своего суперкласса

Ответы

Ответ 1

Если все, что мы хотим, это сделать некоторые дополнительные функции после того, как selected свойство было установлено, мы можем просто переопределить, чтобы добавить наблюдателя свойства:

override var selected: Bool {
    didSet {
        if self.selected {
            // do something
        }
    }
}

Обновленный ответ для Swift 5:

override var isSelected: Bool {
    didSet {
        if self.isSelected {
            // do something
        }
    }
}

Если мы заботимся о том, что предыдущее значение selected было, мы можем получить доступ к нему через oldValue:

didSet {
    if self.selected == oldValue {
        return
    }
    // do something
}

Обновленный ответ для Swift 5:

didSet {
    if self.isSelected == oldValue {
        return
    }
    // do something
}

И не забывайте, что мы можем использовать willSet если нам нужно что-то сделать непосредственно перед изменением значения.


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

class ClassA {
    var _foo: Int = 0
    var foo: Int {
        set(newValue) {
            println("Class A setting foo")
            self._foo = newValue
        }
        get {
            return self._foo
        }
    }
}

class ClassB: ClassA {
    override var foo: Int {
        willSet {
            println("Class B will set foo")
        }
        didSet {
            println("Class B did set foo")
        }
    }
}

class ClassC: ClassB {
    override var foo: Int {
        willSet {
            println("Class C will set foo")
        }
        didSet {
            println("Class C did set foo")
        }
    }
}

Теперь, если мы создадим объект ClassC и установим его свойство foo:

var c: ClassC = ClassC()  
c.foo = 42

Мы получаем следующий вывод:

Class C will set foo
Class B will set foo
Class A setting foo
Class B did set foo
Class C did set foo

Итак, важно отметить несколько вещей из этого...

  • Дочерний класс willSet вызывается перед его родительским willSet.
  • Дочерний класс didSet вызывается после родительского didSet.
  • Создание переопределения ради добавления наблюдателей свойств не заменяет никаких наблюдателей свойств в родительских классах.

Первые два пункта имеют смысл. И на самом деле, это делает наблюдателей за недвижимостью гораздо привлекательнее. По сути, Свифт заставляет нашу руку подниматься и опускаться по иерархии соответствующим образом и красиво разделяет ее на два отдельных метода. Swift также не позволяет нам (я полагаю) переопределить свойство родительского класса, но также позволяет наблюдать изменения этого свойства - это намного лучше, чем подход Objective-C.

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