0

I've recently begun to discover what can be done with KVO and I'm refactoring some of my code and saving a lot of lines at the same time. I do face one issue that is so general that it makes me wonder whether a certain pattern is recommended.

In some cases I load a new view controller that needs to represent data from an already initialized model. On -viewDidLoad I would register for KVO:

[_model addObserver:self
         forKeyPath:kSomeKey
            options:NSKeyValueObservingOptionNew
            context:(__bridge void *)(_model)];

and change my interface when values change:

 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                        change:(NSDictionary *)change 
                       context:(void *)context
{
if ([keyPath isEqual:kSomeKey] && context == (__bridge void *)(_model)) {

    [self updateSomeInterfaceElement];

}

Unfortunately and understandably, the view is not updated with current values from the model when I load my new view.

Is my best option to call -updateSomeInterfaceElement in -viewDidLoad? It doesn't seem to be a big deal like this, but when listening for 10-20 properties, it looks very inefficient (especially since all my -updateSomeInterfaceElement methods are mostly 1 line only, so no need to make them into a separate method). Is there any way to circumvent this, or is there a more elegant solution?

Tom
  • 2,674
  • 1
  • 25
  • 33
  • Is there a reason you are adding a _player context when registering an observer but trying to act on the notification if the context is a _model object? Unless _player == _model, they are completely difference contexts which is why the test you are applying is failing. – Abizern Jun 17 '13 at 13:41
  • @Abizern, sorry, typo. Fixed. They were indeed intended to be the same. – Tom Jun 17 '13 at 16:48

1 Answers1

1

You want to change your options to include NSKeyValueObservingOptionInitial. This will cause KVO to fire a notification when you add the observer, providing the observer with the "initial" value.

Also, as an aside, you should get in the habit of calling super if observeValueForKeyPath:... is called for a notification you didn't sign up for. Also, it's a bit more bulletproof to avoid using "live" pointers in the role of KVO contexts (since a future object could have the same pointer if the current object is deallocated.) I generally prefer to use a pattern like this:

static void * const MyObservationContext = (void*)&MyObservationContext;

- (void)viewDidLoad
{
    // ... other stuff ...
    [self addObserver:self forKeyPath:@"model.someKey" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:MyObservationContext];
    // ... other stuff ...
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{
    if (context == MyObservationContext)
    {
        // Do stuff
    }
    else
    {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}
ipmcc
  • 29,581
  • 5
  • 84
  • 147