What is the KVO

Key Value Coding (KVO) is an informal protocol that provides a way to indirectly access attributes of objects by identifying them with strings. The way to access object properties directly is to call accessor methods, or to use instance variables directly.

KVO is a relatively basic technology point and is often used interactively with other technologies. When using Cocoa binding, KVO, and Core Data, you need KVC technology.

Access methods, as the name implies, are the methods used to set and obtain object data model attribute values. There are two basic access methods. The first is a getter, which returns the value of a property. The second is the setter, which sets the value of the property. You might be confused and say I haven’t seen or used these methods because Foundation has implemented them for you by default.

Here are some examples:

@interface People : NSObject { NSString *_name; } @property (nonatomic, assign) NSUInteger age; // attribute @end // call _name = @ in program"WellCheng"; self.age = 22; // The above code is equivalent to [selfsetAge: 22];
_age = 22;
Copy the code

In the above code, the instance variable _name is directly used and assigned. Setter accessors are used when the age property is called. I won’t go into more details about accessors, attributes, assign keywords, etc., just understand that KVC is about accessors.

It is important to have a KVC-compatible accessor in your application, which allows Data to be encapsulated and facilitates integration with Cocoa bindings, KVO, and Core Data, and also significantly reduces code.

Simplify code with KVC

Suppose there is a need in a method to return different instance variable values of objects based on arguments.

- (id)valueForPeople:(People *)p withParam:(NSString *)identifier {
    if [identifier isEqualToString: @"name"] {
        return p.name;
    }
    if [identifier isEqualToString: @"age"] {
        return p.age;
    }

    // ...
}
Copy the code

If the People class has many attributes, the method will become very long. Here we use KVC to simplify:

- (id)valueForPeople:People(People *)p withParam:(NSString *)identifier {

    return [p valueForKey:identifier];

}

Copy the code

KVC is done in one sentence. Zambrotta da ~

KVO basics

Keys and key Paths

A key identifies a property of an object, usually the method or property name of an accessor. For objects of the People class, these can be name, age, birthdayDate, and so on.

A Key Path is a dotted string used to retrieve deeper attributes. Suppose birthdayDate is a Date type and the Date class also has attributes such as Year, month, and day. Birthdaydate. day is the key path. In layman’s terms, the key path is intended to make it easier to obtain higher-level attributes. If you can only obtain the first-level attributes of an object, then KVC is of little value. Without keyPath, our code might look something like this:

    [[self valueForKey:@"birthdayDate"] valueForKey:@"year"];
Copy the code

If it’s like two layers above it’s fine, if it’s multiple layers, it’s pretty inelegant code. Imagine a long string of valueForKey –!

Use KVC to get property values

ValueForKey: Returns the value of the specified key. If the object does not have an accessor method or instance variable for the key, the object calls its own valueForUndefinedKey method, which by default is implemented to throw an NSUndefinedKeyException exception. Subclassing this method overrides this default behavior.

In practical use, we generally need to implement this method to do some fault tolerant processing.

DictionaryWithValuesForKeys: this method is better, it will return a dictionary, the key is the key for the incoming, key corresponding to the value of single call valueForKey: results.

If the key passed in is nil, undefined is used to run the exception. If you need to return nil in an array, you need to use the NSNull class to wrap it.

If the key path returns a value for multiple objects, all of them will be returned.

Use KVC to set property values

The setValue:forKey: method sets the value of the specified key. This method unpacks the NSValue wrapper by default and is used to handle constants and structs. Also, if the key does not exist, then send the default setValue: forUndefinedKey: news, news of the default implementation is also throw exceptions.

SetValuesForKeysWithDictionary: method is used for a group of key Settings. There is a case where the value of a non-object is set to nil, in which case the setNilValueForKey: method itself is called. The default implementation of this method is still throwing exceptions, so special handling is required if there is a special need for this. This is what we do when we send this method to a constant or a non-object structure, we convert it, for example, send nil for Double, set Double to 0, BOOL to false, and we can use it as we see fit.

Maybe you have a requirement that you pass in a key that is nil, and in that case, you use the NSNull class. KVC automatically converts [NSNull null] to nil for the call.

Point syntax with KVC

You might be a little confused about the dot syntax in keyPath and the dot syntax in self, but there’s no relationship between the two. The dots in keyPath are used to distinguish element boundaries, but they just happen to be separated by dots at the time. The dot in self is syntactic sugar, just for convenience, because it’s ugly to write curly braces. It’s still a method call in the end. namely

self.birthdayDate.year = @ "1993";
/ / is equivalent to
[[self birthdayDate] year] = @ "1993";

// Of course, if you want to use KVC method for simple assignment, it is not impossible
// The following call has the same result as above
[self setValue: @ "1993" ForKeyPath:@"birthdayDate.year"];
Copy the code

KVC and access methods

In order for KVC to find the exact access method, you need to implement the corresponding access method for KVC. After sending a valueForKey message to a class, KVC must be able to find the corresponding implementation.

Common access patterns

The method that returns the attribute value is of the format -, and the method returns an object, constant, or structure. -is Indicates a Boolean attribute. The BOOL type is special here.

The other thing to note is that for non-object property values, if they’re set to nil, you have to do something special. Subclass the setNilValueForKey method and make special judgments.

Collection accessor methods in one-to-many relationships

While you can still handle the properties of a pair of relationships with – and -set:, this is not very efficient because you need to unpack the set type before performing the operation. So the best approach is still to provide additional accessor methods.

For example, in the case of the Person class, the friendNames attribute is a collection of individual names, which is a typical one-to-many relationship. For its access:

  • Indirect: Use KVC to get a collection property, such as an NSArray object, and then operate on that object
  • Direct: Implements the method model provided by Apple for access purposes.

By implementing the collection access method, we can simulate an object that looks like a collection outside the class. This way, we implement the associated KVC collection methods inside the class, and outside the class, when called, will never feel that the class uses KVC implementation inside the class.

These ideas need to be implemented in concrete code to appreciate the KVC features.

There are two very different types of collection accessors.

Ordered set

In an ordered set relationship, there are operations such as summation, fetch, addition, and substitution. Usually this relationship is an instance of NSArray or NSMutableArray.

Getter

To support read-only access properties:

  • -countOf, mandatory, similar to NSArray’s count method.
  • -objectinatIndex: or -atIndexes: must be implemented, equivalent to the objectAtIndex: and objectsAtIndexes: methods of the NSArray class.
  • Range: the get. Optional, but can get additional benefits. Equivalent to NSArray getObjects:range:.

Mutable Index Accessor

For mutable versions, just implement a few additional methods.

  • -insertObject:inAtIndex: or-insert :atIndexes: Implements at least one index.
  • -removeObjectFromatIndex: or -removeatIndexes: Implements at least one index.
  • – replaceObjectInAtIndex: withObject: the or – replaceAtIndexes: with: optional, achieved can improve the performance.

As you can see, each of these methods has an implementation in NSMutableArray.

Unordered accessor pattern

Unordered accessor methods provide a set of access mechanisms for mutable objects. The object is most likely an instance of NSSet or NSMutableSet.

Getter The method to be implemented

  • -countOf
  • -enumeratorOf
  • -memberOf:

Mutable is a method that needs to be implemented

  • -addObject: or -add:
  • -removeObject: or -remove:
  • -intersect:

The Key Value validation

KVC provides a consistent API for validating attribute values. The validation mechanism provides a class that gives you the opportunity to accept a value, offer a replacement value, or deny a new value and give the reason for the error.

By validating methods, when the setValueForKey method passes in a new value, we have a chance to check that value and do some processing. In this way, the validation of values is centralized in the validation method, and the external business logic processing becomes clear.

Here’s an example:

Verify method naming conventions

The validation method is named in the format validate:error:

Name - (BOOL)validateName:(id *)iovalue error:(NSError **)error {Copy the code

Implement a validation method

The validation method above provides a reference to two parameters: the value to validate and the error message to return.

There are three possible outcomes for the above approach:

  1. Validation succeeds, returns YES and does not change the error object.
  2. If the value fails validation and a valid value cannot be created from it, return NO with a specific NSError attached
  3. The ability to create the correct value from the incoming value and return it. For details, please refer to the example in the official website documentation:
-(BOOL)validateName:(id *)ioValue error:(NSError * __autoreleasing *)outError{
    // The name must not be nil, and must be at least two characters long.
    if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 2)) {
        if(outError ! = NULL) { NSString *errorString = NSLocalizedString(@"A Person's name must be at least two characters long"The @"validation: Person, too short name error");
            NSDictionary *userInfoDict = @{ NSLocalizedDescriptionKey : errorString};
            *outError = [[NSError alloc] initWithDomain:PERSON_ERROR_DOMAIN 
                      code:PERSON_INVALID_NAME_CODE
                      userInfo:userInfoDict];

        }
        return NO; 
    }
    return YES; 
}
Copy the code

If the value fails validation, you first need to check that the outError argument is nil, and if not, you need to set it to the correct value.

Calling validation methods

Can invoke this method directly or through validateValue: forKey: error: the specified key. The default is to find and match the key. If a corresponding method is found, it is returned as the result. If it is not found, YES is returned as the result.

Automatic validation

In general, validation methods are not called automatically, only when CoreData is used and data is saved.

The validation method gives us an opportunity to correct errors, such as the name string passed in to be checked, where we can filter out Spaces and return names with no Spaces. Check whether there are illegal strings. If there are illegal strings, return NO to indicate that the authentication fails.

Validation of a constant

The validation method defaults to objects; constants and structs need to be handled separately.