Search code examples
objective-ccocoakey-value-observing

(KVO) Is it possible to remove all observers of an object, without knowing who is observing?


Here's my current scenario: Have an object, and I make a copy of it so I can modify properties w/o touching the original. When I'm done with the copy it gets dealloc'd, but I get these warnings:

An instance 0x6080001250a0 of class LineGraphic was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x618000043ea0> (
<NSKeyValueObservance 0x6180000c1ce0: Observer: 0x6000001834d0, Key path: points, Options: <New: NO, Old: YES, Prior: YES> Context: 0x0, Property: 0x600000056fe0>
<NSKeyValueObservance 0x6180000c1e30: Observer: 0x6000001834d0, Key path: shadow, Options: <New: NO, Old: YES, Prior: YES> Context: 0x0, Property: 0x600000057460>
<NSKeyValueObservance 0x6180000c9bc0: Observer: 0x6000001834d0, Key path: secondHeadStyle, Options: <New: NO, Old: YES, Prior: YES> Context: 0x0, Property: 0x60000005d7c0>
<NSKeyValueObservance 0x6180000c1ff0: Observer: 0x6000001834d0, Key path: lineStyle, Options: <New: NO, Old: YES, Prior: YES> Context: 0x0, Property: 0x600000051640>
<NSKeyValueObservance 0x6180000c1f10: Observer: 0x6000001834d0, Key path: fillColor, Options: <New: NO, Old: YES, Prior: YES> Context: 0x0, Property: 0x600000054f70>
<NSKeyValueObservance 0x6180000c1b20: Observer: 0x6000001834d0, Key path: strokeWidth, Options: <New: NO, Old: YES, Prior: YES> Context: 0x0, Property: 0x600000054d30>
<NSKeyValueObservance 0x6180000c2140: Observer: 0x6000001834d0, Key path: strokeColor, Options: <New: NO, Old: YES, Prior: YES> Context: 0x0, Property: 0x6000000548b0>
<NSKeyValueObservance 0x6180000c1d50: Observer: 0x6000001834d0, Key path: firstHeadStyle, Options: <New: NO, Old: YES, Prior: YES> Context: 0x0, Property: 0x600000055060>
<NSKeyValueObservance 0x6180000c1ea0: Observer: 0x6000001834d0, Key path: opacity, Options: <New: NO, Old: YES, Prior: YES> Context: 0x0, Property: 0x6080002421f0>
<NSKeyValueObservance 0x6180000c1f80: Observer: 0x6000001834d0, Key path: visibleOpacity, Options: <New: NO, Old: YES, Prior: YES> Context: 0x0, Property: 0x608000247560>
<NSKeyValueObservance 0x6180000ca090: Observer: 0x6000001834d0, Key path: rotation, Options: <New: NO, Old: YES, Prior: YES> Context: 0x0, Property: 0x60800024a590>
)

This is how I copied my object:

ImageDocument *selfCopy = [self copy];

I believe that when I am doing this, all of the observers for the LineGraphic property also observe the copy of LineGraphic in the copy of self. Then when selfCopy gets dealloc'd, these warnings come up.

I figured the best solution would just be to remove the observers-- because in the case of the copy, they don't need to be there anyways.

From searching online, I could only find ways to remove all observations an object is making. But I need it the other way, to remove all observers from an object. Also a lot of resources always reference removing observers for NSNotifications, whereas I need to remove observers for key paths (KVO)

I was thinking if all else fails, I could iterate over all of the observers of the observed object (LineGraphic)-- but the problem is that one: there are a lot of observers, and two: if there are more observers added later, they would have to add the removal of them here in this code. So this solution really wouldn't work well


Solution

  • Is it possible to remove all observers of an object, without knowing who is observing?

    Yes, BUT...

    Your problem is that the KVO model is designed around the idea that the observer is responsible for both registering and de-registering as an observer; it is not the observed objects responsibility; and you are trying to usurp that model.

    The removeObserver methods will throw an exception if the object being removed is not an observer; if you remove an observer yourself then the observer will not know, will at some point attempt to de-register, and you will get an exception, not good.

    So think very carefully about whether this is what you really want to do.

    Still want to do it?

    I believe that when I am doing this, all of the observers for the LineGraphic property also observe the copy of LineGraphic in the copy of self.

    This is not the expected usual behaviour, KVO is used to watch a specific instance and copying that instance creates a new distinct object. It is possible to implement copying so the observers are copied, but that would be confusing in general and is not recommended - the observers will not be aware they are now observing a different object, and so cannot later de-register themselves...

    Still want to do it?

    Well you have been warned... think overriding, the details are left to you.

    An alternative suggestion:

    Don't usurp KVO, work with it.

    Add a method to your class which updates an instance (your original object) from another instance (your replacement object). This method can update the backing variables for the properties directly, without calling the setters, and call methods from KVO's willChange & didChange groups such that no notifications are made until the whole object has been updated.

    Now you can create your "replacement", and then update the original. Your observers will continue to observe the same object, and when the time comes de-register themselves.

    HTH