1

Say I have a list of model objects and a controller object which is interested in property changes to the individual model object. When I add the object, I use addObserver for each key it is interested in using KVO between the controller and the model object. Now, when one of the observed objects goes away, I need to tell the view controller to stop observing the changes on this individual object for the particular key.

What is an elegant way of doing this? The best way I can think of is to add a new key kRemoveObject which I call addObserver on in my controller that gets triggered right before the object gets deleted. Then I remove all the observed keys, including kRemoveObject for the particular instance. Does anyone know of a cleaner way? This seems a little bit cumbersome.

possen
  • 8,596
  • 2
  • 39
  • 48

3 Answers3

0

Off the top of my head:

Enable the observed-object to obtain a reference to the controller. Have the observed-object call the removeObserver:forKeyPath: functions. The controller would have to provide a list of keyPaths.

(EDIT: A super clean way of doing this would be defining a delegate protocol (with an "imDying" method) for your observed-object, add a delegate property to your observed-object, setting the controller as the delegate, and having the observed-object call the delegate's "imDying" method in the dealloc. Replace "imDying" with a better name, heh.)

Or:

Have the controller manage the lifespan of the observed-object, allowing you to remove itself prior to the observed-object deallocs.

MechEthan
  • 5,703
  • 1
  • 35
  • 30
  • Thanks for the suggestions, my issue with a delegate approach is that it is using a different mechanism than KVO and every object that wanted to observe it would need to adopt that protocol. For the second suggestion, the job of the model is to manage the lifespan of the objects. Only the model really knows when something goes away and the controller should be driven from changes in the model. – possen Jan 20 '12 at 00:31
  • - Removed my mention of NSNotificationCenter, as that's not used by KVO. – MechEthan Jan 20 '12 at 00:43
  • Guess what I am getting at is it seems like there should be a way to do this with KVO techniques. So maybe the solution I outlined is the only way to do it. – possen Jan 20 '12 at 00:43
  • - For the second portion, correct, the ViewController should not manage model object lifespans. I misunderstood your post, and assumed you meant a "Controller" as described in the MVC (Model-View-Controller) paradigm. That kind of Controller SHOULD manage things as model-object lifespans, and it sounds like you should consider implementing it, and using it to help your View deal more with View details and less with the model-object lifespans. – MechEthan Jan 20 '12 at 00:47
  • Yeah if you want to stick with KVO and not introduce any other managing classes, then your approach seems good to me! – MechEthan Jan 20 '12 at 00:59
0

With KVO the observer should be keeping a reference of what it is observing. If it does this, then the object can never 'go away' (if you mean be released), because the observer has a reference too it.

If you want to stop observing it, then do so from the observer.

If you are just waiting for one value to change, then when you get the notification - remove the observer. (Take precautions not to remove the observer if it is already removed).

In dealloc of your observer, you will clean up - removing all your remaining observations on the objects.

It might be a case of keeping an array of observed objects in the observer to keep track of them - i've seen this used, but I don't like it and it isn't very flexible.

What I do like, is a class I have called KVOHelper. You create this in the observing class and pass it the object you are observing, the observer and the key path. It's a wrapper around KVO. You can remove the observer if you like - or just release the KVOHelper and in it's dealloc routine it removes the observer before releasing everything. It ensures that you can't remove the same observer more than once (which would raise exception). It also comes bundled with a KVOHelperSet for managing multiple KVOHelpers. I can't take credit for writing this class - I got it from a guy I worked with on a project. But I use it all the time now - and it works well. The principle is straightforward and you should be able to create your own.

bandejapaisa
  • 26,576
  • 13
  • 94
  • 112
0

I found this very elegant way of doing it and it solves one of the things that has always bothered me about KVO which is the large "switch statement" that KVO adds with observeValueForKeyPath. KVO+Blocks is very cool, it eliminates that "switch statement" and it automatically handles removal of the observer so no need to call removeObserver when using ARC (if you are doing your own memory management then I think you need to call removeObserverWithBlockToken, although I have not tried it).

http://blog.andymatuschak.org/post/156229939/kvo-blocks-block-callbacks-for-cocoa-observers

Code here: https://gist.github.com/153676

One thing to be careful of, but is true of blocks in general, is if you reference self in your block. You need to do this:

__block blockSelf = self; 

if you don't you will end up with a retain cycle. (see Retain cycle on `self` with blocks for more details).

One other thing, it works with ARC if you put -fno-objc-arc on the file. (See this for details How can I disable ARC for a single file in a project?)

I hope that Apple adds something like this to the SDK.

Community
  • 1
  • 1
possen
  • 8,596
  • 2
  • 39
  • 48