Swift: Overriding Didset Results in a Recursion

Override property in Swift subclass

Let’s reduce the example:

class BaseClass {
var surname: String? {
didSet { print("BaseClass \(surname)") }
}
}

class SubClass: BaseClass {
override var surname: String? {
didSet { print("SubClass \(surname)") }
}
}

Then:

let object = SubClass()
object.surname = "Jones"

Will produce:

BaseClass Optional("Jones")

SubClass Optional("Jones")

The above is not overriding the stored property, surname, with another stored property. There is only the stored property of the base class and the subclass is simply adding its own observer to this property. I refer you to The Swift Programming Language: Inheritance: Overriding, which says:

Overriding Property Observers


You can use property overriding to add property observers to an inherited property. This enables you to be notified when the value of an inherited property changes, regardless of how that property was originally implemented.

In your example of name, you are overriding the computed property with the subclasses’ own computed property. Likewise, in your example of telephoneSet, you are also overriding the method with the subclasses’ own method. But with surname, you’re not overriding the base classes’ property, but merely letting the subclass add an observer to the base classes’ stored property.

In Swift, does resetting the property inside didSet trigger another didSet?

I also thought, that this is not possible (maybe it wasn't in Swift 2), but I tested it and found an example where Apple uses this. (At "Querying and Setting Type Properties")

struct AudioChannel {
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 {
didSet {
if currentLevel > AudioChannel.thresholdLevel {
// cap the new audio level to the threshold level
currentLevel = AudioChannel.thresholdLevel
}
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
// store this as the new overall maximum input level
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
}
}
}

And below this piece of code, there is the following note:

In the first of these two checks, the didSet observer sets currentLevel to a different value. This does not, however, cause the observer to be called again.

What is the purpose of willSet and didSet in Swift?

The point seems to be that sometimes, you need a property that has automatic storage and some behavior, for instance to notify other objects that the property just changed. When all you have is get/set, you need another field to hold the value. With willSet and didSet, you can take action when the value is modified without needing another field. For instance, in that example:

class Foo {
var myProperty: Int = 0 {
didSet {
print("The value of myProperty changed from \(oldValue) to \(myProperty)")
}
}
}

myProperty prints its old and new value every time it is modified. With just getters and setters, I would need this instead:

class Foo {
var myPropertyValue: Int = 0
var myProperty: Int {
get { return myPropertyValue }
set {
print("The value of myProperty changed from \(myPropertyValue) to \(newValue)")
myPropertyValue = newValue
}
}
}

So willSet and didSet represent an economy of a couple of lines, and less noise in the field list.

Swift variable observers not called before super.init called

They are talking about following scenario:

class A {
var p: Bool {
didSet {
print(">>> didSet p to \(p)")
}
}

init() {
p = false // here didSet won't be called
}
}

class B: A {

override init() {
// here you could set B's properties, but not those inherited, only after super.init()
super.init()
p = true // here didSet will be called
}
}

B()

It will print following:

>>> didSet p to true

While to you it might seems natural, the documentation has to explicitly document this behavior.

Change the value that is being set in variable's willSet block

It's not possible to mutate the value inside willSet. If you implement a willSet observer, it is passed the new property value as a constant parameter.



What about modifying it to use didSet?

var people:[Person] = [Person]()
{
didSet
{
people.sort({ (a:Person, b:Person) -> Bool in
return a.age > b.age
})
}
}

willSet is called just before the value is stored.

didSet is called immediately after the new value is stored.

You can read more about property observers here
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html

You can also write a custom getter and setter like below. But didSet seems more convenient.

var _people = [Person]()

var people: [Person] {
get {
return _people
}
set(newPeople) {
_people = newPeople.sorted({ (a:Person, b:Person) -> Bool in
return a.age > b.age
})
}

}

What is the use of open variables over public variables which are stored?

You can override a property but not with a stored property. When overriding properties, you have to use a computed property, e.g.:

A class with a stored property:

open class God {
open var hasSuperPowers = true
}

The property can be overriden only with a computed property:

class HalfGod: God {
override var hasSuperPowers: Bool {
didSet {
print("\(oldValue) -> \(hasSuperPowers)")
}
}
}

or

class HalfGod: God {
var hasPowers: Bool = false

override var hasSuperPowers: Bool {
get {
return hasPowers
}
set {
hasPowers = newValue
}
}
}

You can override properties from other modules only if they are open. public properties cannot be overriden from other modules.



Related Topics



Leave a reply



Submit