Series of articles:OC Basic principle series.OC Basic knowledge series

In this article, we will introduce the underlying implementation of KVC

KVC definition

The full name of KVC is key-value Coding, which is also known as key-value Coding in Chinese. Key-value Coding is a mechanism enabled by NSKeyValueCoding informal protocol. Objects use this protocol to access the object properties indirectly (that is, through a key value). This indirect access mechanism complements the direct access provided by instance variables and their associated accessor methods.

KVC commonly used API

Commonly used method

  • 1. Set the value by key

  • 2. Set/value using keyPath

Other related methods

KVC set value and underlying principle

In everyday assignments, we often use setter methods or KVC assignments, as shown in the following figure:Let’s take a look at the underlying assignment of the most commonly used setValue:forKey:.

1. First let’s look for the method implementation

We found that this method is in the NSObject category, and then we saw that the implementation is in the Foundation framework, so the implementation of this method is not open source.

What should we do at this time? Because the research content is not open source, there is no other way to do research. Previous articles have used many methods, but looking at the source code is just one of them. We can do this by:

  • Clang to.cpp to see how it’s compiled in C++
  • Find the relevant methods through LLVM
  • Disassemble with Hopper and take a look at the code
  • View through GNU code. Download link:gnu(This is to help understand, its code is closest to the official source).
  • Via apple official documentation
  • Download the relevant code from GitHub for analysis

Here we explore the Value setting process through the official documentation: About key-value Coding and GNU code assistance

2. Study the underlying implementation of setValue:forKey: assignment

When the setValue:forKey: method is called, the underlying call is divided into several steps

  • 1. Check whether setKey, _setKey, _setIsKey (The three set methods are lookups at once, assigned as soon as there is an implementation, and no longer look down). Key is a member variable. If none, proceed to Step 2.
  • 2. If step 1 doesn’t find writing three set method, is to find whether accessInstanceVariablesDirectly return YES (Returns YES by default). If YES is returned, indicating thatIf the SetKey method is not found, it will search for the directly accessed instance variable and assign the value. Members will be searched in the order _key, _iskey, key, and iskey (key refers to the member variable). If the SetKey method is set to NO, this will not be searched. If NO is returned or the above method is not found, proceed to step 3.
  • 3. Come to this stepIf neither setter method nor instance variable is found, the system executes the setValue: forUndefinedKey: method for that objectIf theIf this method is not implemented, an exception of type NSUndefinedKeyException is thrown(method setValue: forUndefinedKey: is required to implement).

To sum up, KVC sets the value through setValue:forKey: method as shown in the figure below (the name of the Person class has been set as an example) :

The underlying principle of KVC value

Similarly, we can explore the principle of assignment through official documentation and GNU code. Let’s examine the underlying changes when valueForKey: is executed.

  • 1. Search for getter methods in the sequence of getKey, isKey, and _Key methods. If found, perform step 5; if not, perform Step 2.
  • KVC looks for countOfKey, objectInKey AtIndex:, and KeyAtIndexes:.
    • If either countOfKey or the other two methods are foundCreate a proxy object for the collection of methods that respond to all NSArray and return that object, NSKeyValueArray, which is a subclass of NSArray.
    • The proxy object will thenAll received NSArray messages are converted to some combination of countOfKey, objectInKey AtIndex: and KeyAtIndexes: messages, which are used to create key-encoded objects.
    • If the original object isIf an optional method called getKey: Range: is implemented, the proxy object will also use this method when appropriate(Note: method names must follow KVC’s standard naming rules, including method signatures.)
    • If not, go to step 3.
  • Step 3: None of the above methods are found
    • At this point would beFind the countOfKey, enumeratorOfKey, and memberOfKey methods simultaneously.
    • If all three methods are found, yesCreate a collection proxy object that responds to all NSSet methodsAnd returns the object, which the proxy object then treatsAll received NSSet messages are converted to countOfKey, enumeratorOfKey, and memberOfKey: some combination of messagesThe object used to create it.
    • If not, go to step 4.
  • 4. Entering this step means none of the above is found
    • At this time will check whether accessInstanceVariablesDirectly class method to YES.
    • If YES, the member variables are searched in the order _Key, _isKey, Key, isKey. If you find the Key you need, go to step 5.
    • Failed to find the Key needed to enter the step 6, if accessInstanceVariablesDirectly to NO also in step 6.
  • 5. Different results are returned based on the attribute value type.
    • If it is a pointer, the result is returned directly.
    • If it is a standard type supported by NSNumber, it is stored in an NSNumber instance and the value is returned.
    • If none of the above types match, it is converted to NSValue and returned.
  • 6. If the corresponding Key is not found in this step, the valueForUndefinedKey method is executed. If the method is not implemented, an exception of type NSUndefinedKeyException is thrown.

To sum up, the process of evaluating by valueForKey: is shown below (name of the Person class has been taken as an example).

KVC several assignment methods

Key-value Coding (KVC) : basic type

The generic type is what we call a string, the basic data type, which can be used as follows:

    LJPerson *person = [[LJPerson alloc] init];
    person.name      = @"LJ_A";
    person.age       = 18;
    person->myName   = @"A";
    // KVC
    [person setValue:@"LJ" forKey:@"name"];
Copy the code

This kind of thing is relatively simple, don’t explain too much here

KVC – Collection type

Collection types can be used as follows:

person.array = @[@"1",@"2",@"3"]; // Person. Array [0] = @"100"; NSArray *array = [person valueForKey:@"array"]; array = @[@"100",@"2",@"3"]; [person setValue:array forKey:@"array"]; NSLog(@"%@",[person valueForKey:@"array"]); NSMutableArray *mArray = [Person mutableArrayValueForKey:@"array"]; mArray[0] = @"200"; NSLog(@"%@",[person valueForKey:@"array"]);Copy the code

We run the codeThe assignment was found successful

Set operator

Collection operators include: array values and traversal, dictionary operations traversal, KVC messaging, aggregate operators, array operators, and nested collections (Array&SET) operations

Access non-object properties

The structure of the body

    ThreeFloats floats = {1.,2.,3.};
    NSValue *value     = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
    [person setValue:value forKey:@"threeFloats"];
    NSValue *value1    = [person valueForKey:@"threeFloats"];
    NSLog(@"%@",value1);
    ThreeFloats th;
    [value1 getValue:&th];
    NSLog(@"%f-%f-%f",th.x,th.y,th.z);
Copy the code

Let’s go ahead and print

Layer access – keyPath

LGStudent *student = [LGStudent alloc]; Student. Subject = @" study hard "; person.student = student; [person setValue:@"Swift" forKeyPath:@"student.subject"]; NSLog(@"%@",[person valueForKeyPath:@"student.subject"]);Copy the code

KeyPath is also called routing, so let’s run the codeWe assign swift to student.subject via keyPath.The reason why KVC is divided into the above several is explained from the official documents

Custom KVC

Above we talked about the KVC set assignment and the value process, so the custom KVC is actually to implement the above mentioned methods. So let’s create an NSObject class LJKVC

Custom KVC assignment

General idea

  • 1. Check whether the key value exists. If the key value does not exist, return directly.
  • 2. Search for setter methods: setKey, _setKey, and setIsKey.
  • 3. Determine whether accessInstanceVariablesDirectly to YES for YES to continue with the following process, to NO exception is thrown directly.
  • 4. Assign values to indirectly accessed variables (only once) in _Key, _isKey, Key, and isKey order.
    • Defines a mutable array that collects instance variables
    • Get the ivAR by class_getInstanceVariable
    • Object_setIvar assigns the corresponding IVar
  • 5. If no instance variable is found, an exception is thrown

The overall approach is as follows:

The KVC value is customized

General idea:

  • 1. Check the key.
  • 2. Find the corresponding method.Order: getKey, Key, countOfKey, objectInKeyAtIndex: (Key is the passed Key value).
  • 3. Determine whether accessInstanceVariablesDirectly to YES (whether direct assignment instance variables, YES is ok). NO throws an exception. (Throwing an exception is a crash).
  • 4. Indirectly access instance variables in _Key, _isKey, Key, and isKey order
    • Defines a mutable array that collects instance variables
    • Run class_getInstanceVariable to obtain the ivAR
    • Object_getIvar returns the corresponding IVAR value

The overall approach is as follows:That’s it for custom KVC. Complete code:Custom KVC.

conclusion

We talked about the above KVC assignment and value process, but also wrote a custom KVC, the next article will talk about KVO with KVC close concern