Observation doesn't have a concept of equality (i.e. it doesn't require observed types to be Equatable and doesn't do any == checks); instead, it just fires on every write. You can filter out the redundant notifications yourself if you wish (although this might not be what you want: see below).
Also, it prints five times (instead of four, as there are four writes per above) because you're printing in the apply closure, which purpose is to read out (and register for) the value. The reason it prints false two times initially is that it's observing the willSet event (which is undocumented and IMO unintuitive for being the default), but this means that it will read out the value prior the write until the onChange closure returns. Here's the place.
The intended way to use (this flavour of) observation is to perform "invalidation" of sorts by setting dirty flags, scheduling render updates etc. The onChange closure is there to signal about an occurring write in a lightweight form, not for performing any major logic or data diffing within it.