Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is 'didset' called on a property when I set the property of that property?

Tags:

ios

swift

In this code when the text changes, titleEditingChanged is called (as expected). But when it executes the line

investment?.title = sender.text!

it calls the didset{} of Investment. Why?

class InvestmentCell: UITableViewCell {

    var investment: Investment? {
        didSet {
            // setup UI elements from class properties
            textField.text = investment?.title
            valueField.text = investment?.value?.description
        }
    }

    @IBAction func titleEditingChanged(sender: UITextField) {
        investment?.title = sender.text!
    }

    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var valueField: UITextField!
}
like image 570
ziggyzoggy Avatar asked Mar 08 '16 15:03

ziggyzoggy


People also ask

Is didSet called on initialization?

willSet and didSet observers are not called when a property is first initialized. They are only called when the property's value is set outside of an initialization context.

What does didSet mean in Swift?

In Swift, didSet and willSet methods act as property observers. willSet runs a piece of code right before a property changes. didSet runs a piece of code right after the property has changed.

What is a didSet?

didSet is called right after the data is stored and it has a default constant oldValue which shows the previous value that is overwritten. willSet and didSet cannot be used at computed property since set function has already covered their functionalities and getter only stored property cannot be set actually!

What are property observers?

Property Observers. Property observers observe and respond to changes in a property's value. Property observers are called every time a property's value is set, even if the new value is the same as the property's current value. You can add property observers in the following places: Stored properties that you define.


2 Answers

It is called because Investment is probably a struct, not a class. In Swift structs are value types, not reference types as classes. So, structs are not "mutable in place".

It means that whenever you change a struct property a new struct object is allocated to replace the current one, the current object data is copied to the new one except for the changed property that will contain the new value set.

Remember that the compiler does not let you change a struct property whenever you initialize a struct object with a let command (with a class you can do it).

That explains why the observer is called whenever you change a struct property. Once a new struct object is allocated to replace the current one, it will now be stored in another memory block, so its value will be changed and the didSet observer will be called.

PS: It will not happen if you define Investment as a class instead of a struct.

like image 104
Jorg B Jorge Avatar answered Jan 21 '23 02:01

Jorg B Jorge


Property observers of value types (e.g. structures) are called also when an underlying property of the type instance is set; simply since the value of the instance itself is updated. The same does not hold for reference types; as long as the reference itself is not mutated, the property observer will not be called (i.e., the reference itself can be considered the value of reference types).

From Language Guide - Properties - Property Observers we read:

Property observers observe and respond to changes in a property’s value. Property observers are called every time a property’s value is set, even if the new value is the same as the property’s current value


To verify the above, consider the following example:

/* reference type */
class InvestmentC {
    var title: String = "Foo"
}

/* value type */
struct InvestmentS {
    var title: String = "bar"
}

class InvestmentContainer {
    var investmentC : InvestmentC {
        didSet {
            print("did set a property of 'InvestmentC' instance (ref. type)")
        }
    }

    var investmentS : InvestmentS {
        didSet {
            print("did set a property of 'InvestmentS' instance (val. type)")
        }
    }

    init() {
        investmentC = InvestmentC()
        investmentS = InvestmentS()
    }
}

/* Example: property observer called only when setting a property
            of the value type instance 'investmentC'              */
let foo = InvestmentContainer()
foo.investmentC.title = "foobar" // prints: nothing
foo.investmentS.title = "foobar" // prints: "did set a property of 'InvestmentS' instance (val. type)"

Hence, we can infer that your custom type Investment is a value type (a structure), and the didSet property observer of the instance investment of this type (in your UITableViewCell subclass) will be called even if you only set/update underlying properties of investment. If you'd like to avoid this, change Investment into a reference type (class), in which case the didSet property observer will only be changed if the investment instance itself is set/updated.

like image 26
dfrib Avatar answered Jan 21 '23 02:01

dfrib