简体   繁体   中英

Swift didSet does not fire

Suppose I have class ExerciseSet with 3 properties: id, name, isEnabled.

I have an array of objects of this class:

var exerciseSets: [ExerciseSet] = [] {
    didSet {
        ExerciseSet.syncWithPList(updatedSets: exerciseSets)
    }
}

Somewhere in the code I do the following:

exerciseSets[index].isEnabled = !exerciseSets[index].isEnabled

But didSet would not fire in this case. Only if I write like this:

let set = exerciseSets[index]
set.isEnabled = !set.isEnabled
exerciseSets[index] = set

Why is it so? Can I somehow use the former option? The latter one seems to verbose, I hate it.

That is most probably because ExerciseSet is a class, which means it is a reference type.

Just think of reference types variables as storing numbers. These numbers then point to where the actual ExerciseSet is located in memory. In other words [ExerciseSet] is essentially an array of numbers.

When you do this:

exerciseSets[index].isEnabled = !exerciseSets[index].isEnabled

You did not change any of the "numbers" in the array. You only looked for the "number" at a particular index, and you use that "number" to find the ExerciseSet object. After that, you set a property of the ExerciseSet .

As you can see, you did not modify the "number" in the array at all!

On the other hand, your second piece of code calls the didSet because you're telling it to throw away the element at a particular index and put the "number" that points to set at that index. Since you're tsking out an element of the array and putting something back in, you're changing the array itself! Therefore, didSet is called.

Hate is a strong word :)

In this line:

exerciseSets[index].isEnabled = !exerciseSets[index].isEnabled

you're not actually changing anything directly in your exerciseSets array, you are changing something on a specific ExerciseSet item in that array.

Compare that to:

let set = exerciseSets[index]
set.isEnabled = !set.isEnabled
exerciseSets[index] = set

Here you are changing an actual item in your exerciseSets array and therefore didSet is fired.

Can you do something about it? Good question :) You need to update the exerciseSets array in some way or another to force the didSet so I don't think you can do it a lot shorter than the three lines above. I hope I am wrong though.

Yeah, not so much an answer as a comment, hope you can use it anyway.

The didSet trigger applies to the set, but not to its contents. In other words, changing what objects are stored in the set triggers the block, but changing the objects' contents doesn't. That's why your first option doesn't work and the second one does.

Why is it so?

didSet fires when you change value of the property , in example it would be when array itself changes.

Here

exerciseSets[index].isEnabled = !exerciseSets[index].isEnabled

you change property of some object in the array, but array stays the same , and contains same objects. So didSet won't be called.

Can I somehow use the former option?

No, you can't. Use latter.

EDIT: I checked @martin-r advice to use structs for ExerciseSet , and it works, didSet set is called. Because when property value change for structure basically means creation of new structure with all but changed field being copied.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM