2013/11/29

Try Key-Value Observing

MVC design pattern
↑From Cocoa Core Competencies: Model-View-Controller

I have been interested in the MVC design pattern, and I often visit websites about it to learn more.
The MVC pattern needs a flow to notify from Model class to Control class. At first I thought it was easy to use delegate, but there might be SOME C or M classes referring to an observed Model class, therefore notify is better.
Well, delegate can send messages to only one class, I see.

Anyway I tried this KVO(Key-Value Observing) for the first time.

There may be two types of this Key-Value Observing, one is 'automatic' and the other is ‘manual'.


(1) automatic
I tried an automatic way to notify at first. A Controller class observes change of value in a Model class.
At first a Controller class needs setting to observe :
{
    [model addObserver:self forKeyPath:@"counter" options:NSKeyValueObservingOptionNew context:nil];
}
I was surprised that the string set by ‘forKeyPath’ was equal to the property name you want to observe.
New value will be also notified with this NSKeyValueObservingOptionNew option.

And next is the method which the notify receives :
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqual:@"counter"]) {
        int newValue = [[change valueForKey:@"new"] intValue];
        NSLog(@"newValue=%d",newValue);
        //message to View 
    }
}
You might receive many notifies from many classes, so you have to check the ‘keyPath’.
And the new value (or other values set by option) can get as above, since they are sent by NSDictionary object.

Next, a Model class which is observed.
This observing is an automatic one, so you need next method :
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey
{
    BOOL automatic = NO;
        if ([theKey isEqualToString:@"counter"]) {
        automatic = YES;
    } else {
        automatic = [super automaticallyNotifiesObserversForKey:theKey];
    }
    return automatic;
}
I suppose you need one method to actually change the value in the Model class. For example, this sample code counts up a number when you tap a button. So this kind of code will be called from Controller class :
-(void)countUp
{
    self.counter++;
}
If you wrote
_counter++;
to increment the number, this would not work as you expected, you have to use setter when you change the property.

This means you can not notify automatically for a ‘readonly’ property. If you want to observe a readonly property or instances, you have to use manual-notify.


(2)manual
When you notify to Controller class by manual way, you just set ‘NO’ at this method:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
This is just the opposite way in automatic one.
And you also need next process before and after changing the value you want to observe :
{
    [self willChangeValueForKey:@"counter"];
    counter++;
    [self didChangeValueForKey:@"counter"];
}
‘counter’ is an instance valuable. And the string of the key (@“counter”) must be the same as the instance valuable.
Develop | Comments(0) | Trackback(0)
Comment

管理者のみに表示