KVC stands for key-value Coding, commonly known as “key-value Coding”, which allows access to an attribute through a Key

Common API usage

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

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

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

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

Create two new classes YMPerson and YMCat with the following code:

#import <Foundation/Foundation.h>
#import "YMCat.h"

NS_ASSUME_NONNULL_BEGIN

@interface YMPerson : NSObject
@property (nonatomic, assign) int age;

@property (nonatomic, strong) YMCat *cat;
@end

NS_ASSUME_NONNULL_END
Copy the code
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface YMCat : NSObject
@property (nonatomic, assign) int weight;
@end

NS_ASSUME_NONNULL_END
Copy the code

1.setValue:forKey:andsetValue:forKeyPath:The difference between

Set the age of YMPerson and the name of YMCat

YMPerson *person = [[YMPerson alloc] init]; person.cat = [[YMCat alloc] init]; // Use KVC to modify the age attribute [person setValue:@11 forKey:@"age"]; NSLog(@"person.age = %d",person.age); [person setValue:@22 forKeyPath:@"age"]; NSLog(@"person.age = %d",person.age); // [person setValue:@" less "forKey:@"name"]; // error [person.cat setValue:@" less "forKey:@"name"]; NSLog(@"cat.name = %@",person.cat.name); // person setValue:@" forKeyPath:@" // person setValue:@" forKeyPath:@"cat.name"]; NSLog(@"cat.name = %@",person.cat.name); / / moreCopy the code

2. Common scenarios in the project

1) UI operation

[textField setValue:[UIColor grayColor] forKeyPath:@"_placeholderLabel.textColor"];
Copy the code

Custom Tabbar: You can customize a UITabbar object and then create your own view internally and rearrange it internally using the layoutSubviews method. Then replace the Tabbar property of UITabbarController with a custom class via KVC.

2) Dictionary to Model

Status *status = [[self alloc] init]; / / use KVC dictionary model [status setValuesForKeysWithDictionary: dict]; return status;Copy the code

In the dictionary-to-model case, if you assign values one by one in a custom init method, you’ll need to change the assignment statement every time the data changes. However, with the assignment API provided by KVC, it is possible to batch assign data. Assume that we have the following JSON data and define the Status, the outside world through setValuesForKeysWithDictionary: method for assignment Status.

3) Use in combination with KVO

According to the nature of KVO, it generates new subclasses and overwrites setter methods at run time, sending messages when their contents change. However, this is only triggered by direct assignment to the property. If the property is a container object, add or remove the container object, KVO methods will not be called. It can be used in conjunction with the CORRESPONDING API of KVC to trigger KVO when changes occur within the container. When the container object operation is performed, the collection object can be obtained by key or keyPath, and then the container object can be added or removed to trigger the KVO message notification.

The Key way to

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key; - (NSMutableOrderedSet *) mutableOrderedSetValueForKey: (nsstrings *) key API_AVAILABLE (macos (10.7), the ios (5.0), Tvos watchos (2.0), (9.0)); - (NSMutableSet *)mutableSetValueForKey:(NSString *)key;Copy the code

KeyPath method:

- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath; - (NSMutableOrderedSet *) mutableOrderedSetValueForKeyPath: (nsstrings *) keyPath API_AVAILABLE (macos (10.7), the ios (5.0), Tvos watchos (2.0), (9.0)); - (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;Copy the code

Code verification:

[self.person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"context-age"];
[self.person1 addObserver:self forKeyPath:@"mutArr" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"context-mutArr"];
Copy the code
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event setValue:@33 forKey:@"age"]; // [_person1.mutarr addObject:@"add"]; NSMutableArray *mutArr = [_person1 mutableArrayValueForKey:@"mutArr"]; [mutArr addObject:@"add"]; }Copy the code

Two, the principle of KVC assignment

1. Search for methods in setKey:, _setKey: order, and call methods to pass parameters if found. 2. Didn’t find the first step is called accessInstanceVariablesDirectly method, this method returns a value to NO direct call when setValue: forUndefinedKey: And throws an NSUnknownKeyException, which goes to step 3 if the method returns YES. The default for this method is to return YES. 3. According to _key, _isKey, key member variables, isKey sequential search, found a direct assignment, and did not find is still call setValue: forUndefinedKey: and throw an exception NSUnknownKeyException.

Process diagram:

3. Value principle of KVC

1. KVC values are found in the order of getKey, iskey, and _key. Call 2. Couldn’t find the same, check the accessInstanceVariablesDirectly method If the return YES; > Member variables can be accessed directly if return NO; > Member variables cannot be accessed directly. 3. If they can be accessed, the system searches for member variables in _key, _isKey, key, and iskey order

Four,

1. Will KVO be triggered by modifying attribute values through KVC?

A: It does. Because KVC also uses setKey

2. Will KVO be triggered if the value of a member variable is modified by KVC?

A: It does. Because KVC internally calls the willChangeValueForKey and didChangeValueForKey methods

3. Does modifying a member variable directly trigger KVO?

A: No. Modify member variables directly, without using setKey

[person setValue:@11 forKey:@"age"]; Person ->age = 11111; // Cannot triggerCopy the code

4, dictionary setValue and setObject difference

setValue: forKey:

  • 1. The method is KVC (key-value encoding). The method is created in NSObject, which means that all OC objects have this method, so it can be used with any class.
YMPerson *person = [[YMPerson alloc] init]; Person setValue:@"mystrict" forKey:@"name"]; person setValue:@"mystrict" forKey:@"name"]Copy the code
  • Value can be nil. If it is nil, the removeObject forkey method is automatically called
  • 3. The key must be a string
  • 4. If the key of valueForKey contains the @ symbol, the @ symbol is automatically removed from the value, and the program crashes

setObject: forKey:

  • 1. Methods are specific to NSMutableDictionary;
  • 2. Value cannot be nil. If value is nil, the program crashes. But value can be [NSNull null], [NSNull null] is an empty object, not nil;
  • 3. The Key object is an ID, not an NSString, but we use NSString a lot.
  • 4. If the key of the objectForKey contains the @ sign, the value is not affected and can be retrieved normally