What is KVC?
KVC
Is Key Value Coding
an 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/setter
access the properties of the object through the method. Mechanisms that allow KVC
indirect access to object properties. Usually we use valueForKey
instead of getter
method, setValue:forKey
instead of setter
method.
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 keyPath
the 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.
Let's analyze the steps:
- The first step is to find
setter
the method of this class first. First,key
the first letter of the parameter will be capitalized. How toKey
find the correspondingsetter
method, includingsetKey
and_setKey
, if found, call this method and pass in the parametervalue
to modify the value. If not When you find the setter method, go back and check the return value of the class method of this+accessInstanceVariablesDirectly
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 "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 CalledwillChangeValueForKey:
anddidChangeValueForKey:
method), if the member variable is not found, an error will be reported and the "setValue:forUndefinedKey:
" method will be triggered.
KVC value
- 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
+accessInstanceVariablesDirectly
the 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,isKey
If 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.
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
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