Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.


KVC is key-value Coding,

Commonly known as “key-value encoding”, a key can be used to access a property.

Common apis are:


- (void)setValue:(id)value forKeyPath:(NSString*)keyPath;

- (void)setValue:(id)value forKey:(NSString*)key;

- (id)valueForKeyPath:(NSString*)keyPath;

- (id)valueForKey:(NSString*)key;

Copy the code

The first two are used to set values, and the last two are values.

Let’s start with a small demo to demonstrate the basic usage of KVC:

Create a Person class.

@interface Person : NSObject @property (assign, nonatomic) int age; @end @implementation Person @end Person *person = [[Person alloc] init]; When we assign a value to a person property, we usually use the set method: Person. age = 10; We get a value of a person, we usually use the get method: person.age actually, we can also use: [person setValue:@10 forKey:@"age"]; To assign the age property of the Person object. NSLog(@"%@", [person valueForKey:@"age"]); To get the age property value of the Person object. Or use [person setValue:@10 forKeyPath:@"cat.weight"]; To assign the age property of the Person object. NSLog(@"%@", [person valueForKeyPath:@"cat.weight"]); To get the age property value of the Person object. So, - (void)setValue:(id)value forKeyPath:(NSString*)keyPath; - (void)setValue:(id)value forKey:(NSString*)key; Is there any difference between these two functions? - (id)valueForKeyPath:(NSString*)keyPath; - (id)valueForKey:(NSString*)key; What's the difference between these two functions?Copy the code
Let's look at another demo: we create another class: @interface Cat: NSObject @Property (assign, nonatomic) int weight; @end makes the Person class have a CAT object. @interface Person : NSObject @property (assign, nonatomic) int age; @property (assign, nonatomic) Cat *cat; Before at sign end we want to assign the weight property of the Cat object of the Person object, so we should do that. Person *person = [[Person alloc] init]; Cat *cat = [[Cat alloc] init]; cat.weight = 10; person.cat = cat; So now you have - (void)setValue:(id)value forKeyPath:(NSString*)keyPath; Methods. We can just set it like this. [person setValue:@10 forKeyPath:@"cat.weight"]; But - (void)setValue:(id)value forKey:(NSString*)key; - (void)setValue:(id)value forKeyPath:(NSString*)keyPath; More powerful. Again: we access the weight property of the Cat object of the Person object. We could write it like this. NSLog(@"%@", [person valueForKeyPath:@"cat.weight"]); And. - (id)valueForKey:(NSString*)key; This one doesn't work. More use, you can write code to try. Since KVC can also implement the set method to modify the attributes of an object in a similar way. So, we have this question.Copy the code

Does modifying properties via KVC trigger KVO?

How to answer this question? Right! Just write code and test it out.

How to write test code? One Person class, one Observer class.

Let the Observer listen for changes in the Age property of the Person object. Note: Change this time

For the age property of the Person object, we use KVC to modify the age property. We don’t use setAge:.

The Observer:

@interface Observer : NSObject @end @implementation Observer - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { NSLog(@"observeValueForKeyPath - %@", change); } @end Person class: @interface Person: NSObject @property (assign, nonatomic) int age; Observer * Observer = [[Observer alloc] init]; Person *person = [[Person alloc] init]; / / add KVO listening [person addObserver: the observer forKeyPath: @ "age" options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL]; // Use KVC to modify the age attribute [person setValue:@10 forKey:@"age"]; ObserveValueForKeyPath - {kind = 1; new = 10; Old = 0} KVC can trigger KVO. We need to look at the setValue: forKey: method. [person setValue:@10 forKey:@"age"]; How setValue: forKey: works Screen Snapshot 2021-10-13 Afternoon 11.10.45. PNG] (HTTP: / / https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8cefbe0dc9db44009a44155efff91f79~tplv-k3u1fbpfcp-water You can override methods, add or subtract member variables, etc. To test... @interface Person : NSObject { @public // int age; // int isAge; // int _isAge; // int _age; } @implementation Person //- (void)setAge:(int)age //{ // NSLog(@"setAge: - %d", age); //} //- (void)_setAge:(int)age //{ // NSLog(@"_setAge: - %d", age); //} //- (void)willChangeValueForKey:(NSString *)key //{ // [super willChangeValueForKey:key]; // NSLog(@"willChangeValueForKey - %@", key); //} // //- (void)didChangeValueForKey:(NSString *)key //{ // NSLog(@"didChangeValueForKey - begin - %@", key); // [super didChangeValueForKey:key]; // NSLog(@"didChangeValueForKey - end - %@", key); / /} / / the default return value is YES / / + (BOOL) accessInstanceVariablesDirectly / / {/ / return YES; } @end can trigger KVO by modifying attributes via KVC? KVO is triggered. KVO. Person * Person = [[Person alloc] init]; person->_age = 10; // KVO cannot be triggered by modifying a member variable directly. KVO can also be triggered by modifying a _age member variable directly through KVC. Why is that? What is the assignment and value process of KVC? How does it work? Overriding willChangeValueForKey: didChangeValueForKey: in the Person class will see that the above method is called when the age property is modified directly through KVC. [person setValue:@10 forKey:@"age"]; Internal calls to the assignment process: [Person willChangeValueForKey:@"age"]; person->_age = 10; [person didChangeValueForKey:@"age"]; Let's look at the principle of the internal call to the value process: valueForKey:! Screen Snapshot 2021-10-13 Afternoon 11.11.14. PNG] (HTTP: / / https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9ea5aa8d27404e628cd7bf8451bd378e~tplv-k3u1fbpfcp-water You can override methods, add or subtract member variables, etc. To test... @interface Person : NSObject { @public // int age; // int isAge; // int _isAge; // int _age; } @implementation Person //- (int)getAge //{ // return 11; //} //- (int)age //{ // return 12; //} //- (int)isAge //{ // return 13; //} //- (int)_age //{ // return 14; / /} / / the default return value is YES / / + (BOOL) accessInstanceVariablesDirectly / / {/ / return YES; //} @end note: setKey... Is the key method to call KVO.Copy the code