Runtime——KVC, KVO principle

What is KVC?

KVCIs Key Value Codingan acronym that means Key-Value Coding. In iOS, a method is provided to indirectly access the properties of the object by using the name of the property (that is, Key). This method can not getter/setteraccess the properties of the object through the method. Mechanisms that allow KVCindirect access to object properties. Usually we use valueForKey instead of getter method, setValue:forKeyinstead of settermethod.

Commonly used APIs:

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key; 

setValue:forKey:和valueForKey:It can only be used to access the properties of the current object, but keyPaththe properties of the properties that can be accessed through the current object can be nested layer by layer.

Simply give an example:

#import <Foundation/Foundation.h>
#import "Car.h"
NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject 
@property (nonatomic, assign) NSInteger age;

@property (nonatomic, strong) Car* car;

@end

NS_ASSUME_NONNULL_END```
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Car : NSObject 

@property (nonatomic, strong) NSString* name;
@end
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    
    
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    Person* person = [[Person alloc] init];
    person.car = [[Car alloc] init];
    // KVC
    [person setValue:@"20" forKey:@"age"];
    NSLog(@"%@", [person valueForKey:@"age"]);
    [person setValue:@111 forKeyPath:@"car.name"];
    NSLog(@"%@", [person valueForKeyPath:@"car.name"]);
}

The underlying principle of KVC

KVC assignment

The bottom layer of the keypath is the same as the key. Let's analyze the process of a key, and the previous picture is very clear on the Internet.
insert image description here
Let's analyze the steps:

  • The first step is to find setterthe method of this class first. First, keythe first letter of the parameter will be capitalized. How to Keyfind the corresponding settermethod, including setKeyand _setKey, if found, call this method and pass in the parameter valueto modify the value. If not When you find the setter method, go back and check the return value of the class method of this +accessInstanceVariablesDirectlyclass (whether you can directly access the member variable, the default is YES), if you cannot directly access it, an error will be reported and the " setValue:forUndefinedKey:" method will be triggered.
  • If the member variables can be accessed, the corresponding member variables will be searched in order. _key,_isKey,key,isKey,If the corresponding member variables are found, the member variables will be assigned directly. (If KVO is set, KVO will be triggered at this time, because KVC is here, also Called willChangeValueForKey:and didChangeValueForKey:method), if the member variable is not found, an error will be reported and the " setValue:forUndefinedKey:" method will be triggered.

KVC value

insert image description here

  • Similar to the above process, except that the getter method is searched first, and searched in the order of getKey, key, isKey, _key
  • If the getter method is not found, go back and check +accessInstanceVariablesDirectlythe return value of the class method of the class (whether you can directly access the member variable, the default is YES), if you cannot directly access it, an error will be reported and the " valueForUndefinedKey:" method will be triggered.
  • If the member variables can be accessed, the corresponding member variables will be searched in order. _key,_isKey,key,isKeyIf not found, an error will be reported and the " valueForUndefinedKey:" method will be triggered.

KVO?

The full name of KVO is Key-Value Observing, commonly known as "key-value monitoring", which can be used to monitor the change of an object's property value.
insert image description here
There are only three key points in using KVO: who is being observed : that is, which object and which attribute value you want to observe change; who is the observer : who observes this object, and you can add KVO to the object and remove the KVO of the object after confirmation; The callback method of the observer : the method triggered after the object property changes.
For example:

#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) Person* person;
@end

@implementation ViewController

- (void)viewDidLoad {
    
    
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.person = [[Person alloc] init];
    self.person.car = [[Car alloc] init];
    // KVO
    // 给person添加观察者
    [self.person addObserver:self forKeyPath:@"age" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:nil];
    // KVC
    [self.person setValue:@"20" forKey:@"age"];
    NSLog(@"%@", [self.person valueForKey:@"age"]);
    [self.person setValue:@111 forKeyPath:@"car.name"];
    NSLog(@"%@", [self.person valueForKeyPath:@"car.name"]);
}

- (void)dealloc {
    
    
    // 移除person对象的KVO
    [self.person removeObserver:self forKeyPath:@"age"];
}
// 回调方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    
    
    NSLog(@"被观察对象%@的%@属性发生变化:%@", object, keyPath, change);
}
@end

For specific usage and parameter meanings, please refer to my previous blog - Simple use of KVO

insert image description here

The underlying principle of KVO

Since KVO is realized by using Runtime, let's explore how it is realized.
Draw conclusions first.

  • First, use Runtime to create a new subclass for this class at runtime, and point the isa pointer of the instance that uses KVO to this newly created class. At this time, the instance object is no longer the original class, but belongs to the newly created subclass.
  • Next, this class will rewrite some methods of the original class and NSObject class. Combined with the setter method found by KVC before, we can think that the most important thing is to rewrite the instance method setter.
  • The rewritten setter has three main processes: first call willChangeValueForKey: to indicate that the value is about to be modified, then find the setter method of the object: to actually modify the value of the member variable, and finally call didChangeValueForKey: and implement the observer to call the observer callback in this method method.
    You can look at its approximate pseudocode:
@interface NSKVONotifying_Person : Person
@end

@implementation NSKVONotifying_Person

- (void)setAge:(int)age
{
    
    
    _NSSetIntValueAndNotify();
}

// 伪代码
void _NSSetIntValueAndNotify()
{
    
    
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

- (void)didChangeValueForKey:(NSString *)key
{
    
    
    // 通知监听器,某某属性值发生了改变
    [oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}

// 屏幕内部实现,隐藏了NSKVONotifying_MJPerson类的存在
- (Class)class
{
    
    
    return [Person class];
}

- (void)dealloc
{
    
    
    // 收尾工作
}

- (BOOL)_isKVOA
{
    
    
    return YES;
}

@end

insert image description here

Guess you like

Origin blog.csdn.net/chabuduoxs/article/details/126166600