Part ONE: What can KVO do?
1.1 KVO nature
-
- KVO full Key – Value – Observing
-
- KVO observes the properties of an object, registers a specified path, and automatically notifies the observer if the properties of the object are changed. KVO is an observer mode
-
- KVO can only react to attributes, not to methods or actions.
-
- Notifications are automatically sent every time a property value changes, and the developer does not need to implement it manually.
-
- Note: Any object allows you to observe the properties of other objects and receive notifications of changes in the state of other objects.
-
- When you observe an object, a new class is created dynamically. This class inherits from the object’s original class and overwrites the setter methods for the property being observed. Naturally, the overridden setter method is responsible for notifying all observed object values of changes before and after the original setter method is called. Finally, the isa pointer to the object (which tells the Runtime system what the object’s class is) points to the newly created subclass, and the object magically becomes an instance of the newly created subclass.
1.2 KVO example
- Example 1:
_person = [[Person alloc] init]; /** * Add observer ** @param observer * @param keyPath View property name * @param options View property new value, old value, etc. (enumerated values, can be set as required, For example, you can use two terms) * @param context context, which can be nil. */ [_person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; /** * KVO callback method ** @param keyPath Modified property * @param object Object of the modified property * @param change Changed property (old and new values) * @param context Context */ - (void) observeforkeypath :(NSString *) observeforkeypath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {NSLog(@"%@ object %@ property changed: %@",object,keyPath,change); } /** * removeObserver */ - (void)dealloc {[self.person removeObserver:self forKeyPath:@"age"]; }Copy the code
- Example 2: Listen on the contentOffSet property of ScrollView
[scrollview addObserver: self forKeyPath: @ "contentOffset options: NSKeyValueObservingOptionNew context: nil];Copy the code
1.3 KVO syntax
1.// Register observer, implement monitoring; [self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil]; [Observe] 2.// Observe the method, the callback method, where the property changes; - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change Context :(void *)context [remove] 3. [the self removeObserver: self forKeyPath: @ "age");Copy the code
1.4 KVO and runtime
- 1. When observing object A, the KVO mechanism dynamically creates A new name: NSKVONotifying_A is A new class that inherits from object A, and KVO is NSKVONotifying_A that overrides the setter method to observe the property, and the setter method is responsible for calling the original setter method before and after, Notifies all observed objects of changes in property values.
- 2. The isa pointer to the observed object points to an intermediate class instead of the actual class.
Part two: What can KVC do?
2.1 KVC nature
-
- KVC full name NSKeyValueCoding, Chinese: key value coding
-
- Class attributes are accessed by the name of the string (key);
-
- Not by calling Setter and Getter methods;
-
- Object properties are accessed and modified dynamically at run time, not at compile time.
-
KVC shortcomings
-
- Perform less efficiently than setter and getter methods. Because it uses KVC key-value encoding, it must parse the string before setting or accessing the instance variable of the object.
-
- Using KVC breaks the encapsulation of a class.
-
- Hard to troubleshoot error
-
2.2 Function 1: Perform operations on collections
- Example 1: Find the maximum
NSArray *a = @[@4, @84, @2];
NSLog(@"max = %@", [a valueForKeyPath:@"@max.self"]);
Copy the code
- Example 2: An array of Transaction objects with the attribute amount; When we call [a valueForKeyPath:@”@max.amount”], it calls -valueForKey:@”amount” in each element of array A and returns the largest one.
NSArray *a = @[transaction1, transaction2, transaction3];
NSLog(@"max = %@", [a valueForKeyPath:@"@max.amount"]);
Copy the code
2.3 Function 2: Assign values to private variables such as readonly variables – such as private variables
- To modify the private properties of some classes in the system, you must first get the name of the property, which usually starts with an underscore
-
- Need to change private property – Example 1:
@interface Teacher : NSObject { @private int _age; // A private variable can not be changed externally, but can be changed via KVC if you know the name of the private variable; } @property (nonatomic, strong, readonly) NSString *name; @property (nonatomic, assign, getter = isMale) BOOL male; - (void)log; @endCopy the code
- Before using KVC: With general setters and getters, private variables cannot be accessed outside the class, and values cannot be set to read-only variables
- After using KVC:
Teacher *teacher = [Teacher new]; [teacher log]; // set readonly value [teacher setValue:@"Jack" forKey:@"name"]; // teacher.name = @"Jack"; // set private value [teacher setValue:@24 forKey:@"age"]; // teacher.age = 24; [teacher setValue:@1 forKey:@"male"]; [teacher log]; // Get readonly value NSLog(@"name: %@", [teacher valueForKey:@"_name"]); Private value NSLog(@"age: %d", [[teacher valueForKey:@"_age"] intValue]); NSLog(@"male: %d", [[teacher valueForKey:@"isMale"] boolValue]);Copy the code
-
- Need to change private properties – Example 2: Modify TextField placeholder: note keypath
[_textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"]; [_textField setValue: [UIFont systemFontOfSize: 14] forKeyPath: @ "_placeholderLabel. The font"];Copy the code
-
- Need to change private properties – Example 3: Modify UIPageControl image:
[_pageControl setValue:[UIImage imageNamed:@"selected"] forKeyPath:@"_currentPageImage"];
[_pageControl setValue:[UIImage imageNamed:@"unselected"] forKeyPath:@"_pageImage"];
Copy the code
2.4 Function 3: Dictionary to Model
- (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues;
Copy the code
2.4.1 KVC syntax overrides
-
What’s the difference between key and keypath?
-
Key can only accept attributes that the current class has, either its own or inherited from a parent class, such as view.setValue(CGRectZero(),key: “frame”);
-
Keypath: Can accept properties of the current class as well as properties of the current class, i.e. can accept chains of relationships, such as view.setValue(5,keypath: “layer.cornerRadius “)
-
-
Take an example to illustrate question 1:
-
Example: Let’s say person has a property called address and address has a property called town. Now how do we access the town property from Person?
-
Answer: If access through key
id address = [person valueForKey:@"address"]; Id town = [address valueForKey:@"town "]; Keypath = [person valueForKeyPath:@"address.town"];Copy the code
2.4.2 Syntax (Set Values, Set data)
NSNumber - (void)setValue:(id)value forKey:(NSString *)key; Xx - (void)setValue:(id)value forKeyPath:(NSString *)keyPath; // Its default implementation is to throw an exception. You can override this function for error handling. - (void)setValue:(id)value forUndefinedKey:(NSString *)key;Copy the code
2.4.3 Syntax (Get value, Get Data)
- (id)valueForKey:(NSString *)key; - (id)valueForKeyPath:(NSString *)keyPath; // if the Key does not exist and KVC cannot find any fields or attributes related to the Key, this method is called. The default is to throw an exception - (id)valueForUndefinedKey:(NSString *) Key;Copy the code
2.4.4 Other Syntax
// Allows direct access to the instance variable, and returns YES by default. If a class overrides this method and returns NO, KVC cannot access the class. + (BOOL)accessInstanceVariablesDirectly; // This is the set operation API, there are a series of apis, if the property is an NSMutableArray, So you can use this method to return - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key; // if you pass nil to Value during the setValue method, this method is called - (void)setNilValueForKey:(NSString *)key; // Input a set of keys, return the values corresponding to the keys, and then return the dictionary, which is used to transfer the Model to the dictionary. - (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys; // KVC provides a property value validation API, which can be used to check if the value of a set is correct, make a replacement value for an incorrect value, or reject a new value and return the cause of the error. - (BOOL)validateValue:(id)ioValue forKey:(NSString *)inKey error:(NSError)outError;Copy the code