KVC profile

We know how to set and modify properties of objects using setter and getter methods, and we know how to set and modify properties of objects using simplified dot syntax. In fact, Objective-C also supports a more flexible mode of operation that allows indirect manipulation of attributes of objects as strings. The full name for this is Key Value Coding(KVC), or Key Value Coding.

Simple KVC

The most basic KVC is supported by the NSKeyValueCoding protocol. The most basic two methods for manipulating attributes are as follows:

  • SetValue: indicates the value of the attribute. ForKey: indicates the name of the attribute.
  • ValueForKey: Attribute name: Gets the value of the specified attribute.

@property (nonatomic, copy) NSString *name; Set: [object setValue:@”Suxiaoyao” forKey:@”name”] Value: NSString *nameStr = [Object valueForKey:@”name”]

For setValue: property value forKey:@”name”; Code, the underlying execution mechanism is as follows:

  • (1). The program calls “setName: attribute value” first; The code does this through setter methods.
  • (2). If the class does not have a setName: method, KVC will search for the member variable named _name and assign the value to the member variable.
  • (3). If the class has neither a setName: method nor a _name member variable defined, the KVC mechanism will search for the name member variable of the class and assign a value to the name member variable.
  • (4). If none of the above items is found, the system will execute the objectsetValue: forUndefinedKey:Methods. The defaultsetValue: forUndefinedKey:Method raises an exception that will crash the program.

For the “valueForKey: @” name “,” Code, the underlying execution mechanism is as follows:

  • (1). The program calls “name “first; Code to get the return value of the getter method.
  • (2). If the class does not have a name method, KVC will search for the member variable named _name and return the value of the member variable named _name.
  • (3). If the class has neither a name method nor a _name member variable defined, KVC will search for the name member variable of the class and return the value of the name member variable.
  • (4). If none of the above items is found, the system will execute the objectvalueForUndefinedKey:Methods. The defaultvalueForUndefinedKey:Method raises an exception that will crash the program.

Handle nonexistent keys

As mentioned earlier, when properties are manipulated using KVC, they may not exist, and the system simply throws an exception without doing anything special. However, in our actual development, in combination with our business scenario, we always don’t want the program to crash. At this point, we can consider overwriting setValue: forUndefinedKey: and valueForUndefinedKey: methods.

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {

    NSLog(@"The key you set: [%@] does not exist", key);
    NSLog(@"You set value to: [%@]", value);

}

- (id)valueForUndefinedKey:(NSString *)key {

    NSLog(@"Your access key:[%@] does not exist", key);
    return nil;

}Copy the code

This way, when THE KVC operates on a key that does not exist, the KVC mechanism will always call the overridden method to handle it, which makes it very easy to customize its own processing behavior.

Deal with nil values

When KVC is called to set an object’s property, it is legal to try to set the property to nil if the property’s type is an object type (such as NSString), and the program will run fine.

But if the property is of a primitive type (such as int, float, double), try setting the property to nil, Program will collapse caused by the following exception ‘NSInvalidArgumentException’, and from the message can know setNilValueForKey: methods led to this exception. When a program tries to set a nil value for a property that does not accept a nil value, the program automatically executes the setNilValueForKey: method for that object. We can also override this method:

- (void)setNilValueForKey:(NSString *)key {

    // Handle attributes that cannot accept nil
    if ([key isEqualToString:@"price"]) {

        // Apply to your specific business
        price = 0;

    }else{[supersetNilValueForKey:key]; }}Copy the code

We can rewrite this method and do it separately depending on our different business scenarios.

Key Path

KVC also allows us to access objects through relationships. Given that the person object has the property address and the address has the property city, we can access city from person like this:

[person valueForKeyPath:@"address.city"];Copy the code

Note that here we call -value for key path instead of -value for key.

In the KVC protocol, the operation Key path is as follows:

  • SetValue :forKeyPath: Sets the property value based on the Key path
  • ValueForKeyPath: Obtains the property value based on the Key path

Operation of set

An often overlooked FEATURE of KVC is its support for collection operations. For example, we can get the largest value in an array by:

NSArray *a = @[@4The @84The @2];
NSLog(@"max = %@", [a valueForKeyPath:@"@max.self"]);Copy the code

Alternatively, if we have an array of Transaction objects, and the object has the amount attribute, we can get the maximum amount as follows:

NSArray *a = @[transaction1, transaction2, transaction3];
NSLog(@"max = %@", [a valueForKeyPath:@"@max.amount"]);Copy the code

When we call [a valueForKeyPath:@”@max.amount”], it calls -valueForKey:@”amount” in each element of array A and returns the largest one. The official Apple documentation for KVC has a section called Collection Operators that describes similar usage in detail.

KVC summary

With all of this information, you might wonder, why KVC? Can’t you just call setter and getter methods on an object? Does KVC have better performance? In fact, the performance of manipulating objects through KVC is worse than that of operating through setters and getters. The advantage of using KVC programming is that it is more concise and more suitable for refining some general-purpose code. Because KVC allows you to manipulate an object’s properties in the form of a string, which is both a constant and a variable, it is extremely flexible.

Key value Listening (KVO)

During the development of iOS applications, iOS applications usually separate application components into data model components and view components, where the data model component is responsible for maintaining the application’s state data, and the view component is responsible for displaying the state data within the data model component.

For the above design structure, if there is a requirement of the program: when the state data of the data model component changes, the view component can update itself dynamically and display the updated data of the data model component in time.

IOS provides us with an excellent solution: using KVO(Key Value Observing) mechanism.

The KVO mechanism is supported by the NSKeyValueObserving protocol. Of course, NSObject complies with this protocol, so all subclasses of NSObject can use the methods in this protocol, which contains the following common methods for registering listeners:

  • AddObserver: forKeyPath: options: context: register a listener is used to monitor the specified Key path
  • RemoveObserver: forKeyPath: for the specified Key path to delete the specified listener
  • RemoveObserver: forKeyPath: context: in order to specify the Key path to delete the specified listener, more than just a context parameter.

When a property corresponding to the key path of the data model component changes, the view component as a listener will be fired. When fired, the listener will call back its own listening method, which looks like this: observeValueForKeyPath:ofObject:change:context: As a listener, therefore, the view component needs to be rewritten observeValueForKeyPath: ofObject: change: context: method, rewrite the method can get the latest modification of data, so as to use the latest data to update the view components display.

The steps of KVO programming are as follows:

  • Registers listeners for listened objects (typically data model components)
  • Rewrite the listener observeValueForKeyPath: ofObject: change: context: method
  • Removing listeners

KVO instance scenario

We need to listen to a person’s heartbeat and display it on a screen

Let’s define model

@interface Person : NSObject

/ * * heart * /
@property (nonatomic, copy) NSString *heartbeat;

@end

@implementation Person

@endCopy the code

Define this model as a Controller property, instantiate it, listen for its properties, and display it in the current View

@interface ViewController ()

@property (nonatomic, strong) Person *person;

@property (nonatomic, strong) UILabel *heartbeatLabel;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.person = [[Person alloc] init];
    [self.person setValue:@"72" forKey:@"heartbeat"];
    [self.person addObserver:self forKeyPath:@"heartbeat" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];

    self.heartbeatLabel = [[UILabel alloc] initWithFrame:CGRectMake(100.100.100.30)];
    self.heartbeatLabel.textColor = [UIColor redColor];
    self.heartbeatLabel.text = [self.person valueForKey:@"heartbeat"];
    [self.view addSubview:self.heartbeatLabel];

    UIButton *runButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    runButton.frame = CGRectMake(0.0.100.30);
    runButton.backgroundColor = [UIColor redColor];
    [runButton addTarget:self action:@selector(run:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:runButton];

}Copy the code

When a button is clicked, the run method is called to modify the properties of the object

- (void)run:(UIButton *)sender {

    [self.person setValue:@"100" forKey:@"heartbeat"];

}Copy the code

Implement the callback method

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if([keyPath isEqualToString:@"heartbeat"])
    {
        self.heartbeatLabel.text = [change objectForKey:@"new"]; }}Copy the code

Adding and canceling observations come in pairs, so you need to remove the observer at the end

- (void)dealloc {

    [self.person removeObserver:self forKeyPath:@"heartbeat"];

}Copy the code

KVO summary

The KVO encoding is simple to use and is suitable for the view changes caused by model changes. As in the example above, the listener is immediately notified when the value of the property changes.

Hope to help you, if there is any question in the article, welcome to comment on the message ~, thank you for your support ~ welcome to pay attention to, I will update the technical article in my spare time ~