Implementation principle of KVO

Posted by atdawgie on Wed, 17 Jul 2019 19:36:17 +0200

Overview of kvo

Kvo, also known as Key-Value Observing, provides a way for an object to be notified when an attribute of an object changes, allowing the object listening for the change in the value of that attribute to respond to actions through kvo.

kvo implementation principle

How does kvo implement notification objects, in fact, through the powerful runtime runtime mechanism of Objective-C.When you first look at an object, runtime creates a new subclass that inherits the listened class.In this new class, it overrides all observed key s and then points the isa pointer of the object to the newly created class.So the object magically becomes an instance of a new subclass.These overridden methods add code that calls methods that notify observers.The setKey method is triggered when a property of an object changes, but it is overridden and a notification mechanism is added internally.

Validation experiment of kvo implementation principle

  • We create a new Single View Application project.Then create a new Person class and a Dog class
  • Add an attribute age to Person.h.
#import <Foundation/Foundation.h>

@interface Person : NSObject
//Add an age attribute
@property (nonatomic,assign) int age;

@end
  • Add kvo listening method observeValueForKeyPath:ofObject:change:context to Dog.m:
#import "Dog.h"

@implementation Dog
//kvo listening method
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSLog(@"%@Monitored%@Object's%@The value of the property changed:%@",self ,object ,keyPath ,change);
}

@end
  • Add the following code to the ViewController.m file
#import "ViewController.h"
#import "Person.h"
#import "Dog.h"

@interface ViewController ()

@property (nonatomic, strong)Person *person;
@property (nonatomic, strong)Dog *dog;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.person = [[Person alloc] init];
    self.dog = [[Dog alloc] init];

    self.person.age = 10;

    //Self.domain listens for the age property of self.person
    [self.person addObserver:self.dog forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.person.age = 50;
}

From the code above, you can see that once the view is loaded successfully, Dog can listen for changes in Person age properties.

  • When we run the program and click on the blank screen, we see the following output, indicating that we are really able to use kvo to listen for property changes.

  • Then, by breaking points, we see that the Person class is different before and after calling the addObserver:forKeyPath:options:context:method.
    Before adding an observer:

    After adding the observer:

By comparison, we find that when the person object is listened on, the system dynamically creates a subclass NSKVONOtifying_Person inherited from Person at runtime.KVO then overrides the setter method of any observed property in the base class in this derived class to implement a real notification mechanism in the setter method.

- (void)setAge:(int)age

{

[super setAge:age];

[monitor observeValueForKeyPath:@"age"  ofObject:self  change:@{}  context:nil];

}

KVO is a powerful tool, sometimes too powerful, especially with an automatic trigger notification mechanism.Now we know how it works and that using it creates a new class at run time, so performance can have an impact, so use it unless you have to listen for changes in the value of an attribute.This knowledge may help you use it better or make it easier to debug when it goes wrong.

Topics: Attribute