Key-Value Coding and Key-Value Observing in Objective-C

· 2 min read

What is KVO/KVC

KVC(Key-Value Coding) is a coding style which coming from Functional Programming, KVO(Key-Vlaue Observing) is the technology that underlies Cocoa Bindings, and it provides a way for objects to get notified when the properties of other objects are changed.

How does it work

To illustrate how does the KVO, I think there is no better answer from [This Post](https://www.mikeash.com/pyblog/friday-qa-2009-01-23.html)
KVO happened through the power of objective-c runtime. When you observe an object of a particular class for the first time, the KVO infrastructure creates a brand new class (perhaps in the momery) at runtime that subclasses your class. In that new class,
it overrides the set methods for any observed keys. It then switches out the isa pointer (what is the isa pointer????) of your object (the pointer that tells the Objective-C runtime what kind of object a particular blob of memory actually is) so that your
object magically becomes an instance of this new class.
The overridden methods are how it does the real work of notifying observers. (How?) The logic goes that changes to a key have to go through that key’s set method. It overrides that set method so that it can intercept it and post notifications to observers
whenever it gets called. (Of course it’s possible to make a modification without going through the set method if you modify the instance variable directly. KVO requires that compliant classes must either not do this, or must wrap direct ivar access in
manual notification calls.)

KVO Practise

Step 1: In KVO, the variables are set in Key-Value pair, where objective-c provides two ways of doing so:

// Method 1:
[self setValue:@"John" forKey:@"firstname"];


// Method 2:
[self setValue:@"This is a text" forKeyPath:@"someObject.someProperty.text"];

Those two methods can be used with no difference.

Step 2: To monitor whether a variable has been updated, we add an observer:

[variable_1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

Then implement the delegate method:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

The full project looks like this:

// view controller.h

#import <UIKit/UIKit.h>
#import "Item.h"

@interface ViewController : UIViewController 

@property (nonatomic, strong) NSString *myVal;

@end


// view controller.m 
#import "ViewController.h"

static void *context1 = &context1;
static void *context2 = &context2;


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    [self setValue:@"some string" forKeyPath:@"myVal"];
    [self addObserver:self forKeyPath:@"myVal" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:context1];
    [self addObserver:self forKeyPath:@"myVal" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:context2];
    [self setValue:@"some string else" forKeyPath:@"myVal"];
}


-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    
    if(context == context1){
        if ([keyPath isEqualToString:@"myVal"]) {
            NSLog(@"The name of the child was changed.");
            NSLog(@"%@", change);
        }
    }
    
    if(context == context2){
        if ([keyPath isEqualToString:@"myVal"]) {
            NSLog(@"The name of the child was changed.");
            NSLog(@"%@", change);
        }
    }
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end

https://gist.github.com/arkilis/b420249d3987d47d1ee27760a1ba9b92

Few things about the observer:

variable_1: must be class instance, not prime values (int, float, char)
addObserver:self: self is the observing class instance, in the following code, you need to override the method observeValueForKeyPath
forKeyPath: is the key where you set up in Step 1.
options: Apple provides a bunch of options, which tell the observer what can be observed. Here, we tell the observers to capture both old and new values.
context:: context can be employed to differentiate multiple observed values if they have the same key.


Reference

http://www.appcoda.com/understanding-key-value-observing-coding/https://www.mikeash.com/pyblog/friday-qa-2009-01-23.html