KVO
What is the KVO
KVO, full name of Key Value Observing, is an event notification mechanism provided by Apple. Allows an object to listen for changes to specific properties of another object and to receive an event when the change occurs. Observer mode
Because of the way KVO is implemented, it only works for properties, and objects that inherit from NSObject generally support KVO by default
KVO can listen for changes to individual attributes as well as collection objects. Collection objects contain NSArray and NSSet. The proxy object is obtained through methods such as KVC’s mutableArrayValueForKey:, and when the internal object of the proxy object changes, the method that KVC listens for is called back.
Basic use of KVO
There are three main steps
- through
addObserver:forKeyPath:options:context:
Method register observer -
- Observer: An object that listens for property changes. This object must be implemented
observeValueForKeyPath:ofObject:change:context:
Methods.
- Observer: An object that listens for property changes. This object must be implemented
-
- KeyPath: the name of the property to be observed. Be consistent with the name of the property declaration
-
- Options: The callback method that receives the old or new value of the observed property, and the enum type. The system provides us with four methods
-
-
NSKeyValueObservingOptionOld
: change contains the old value before the key change
-
-
-
NSKeyValueObservingOptionNew
: change contains the new value after the key change
-
-
-
NSKeyValueObservingOptionInitial
: change does not contain the value of key, kVO will notify immediately upon registration
-
-
-
NSKeyValueObservingOptionPrior
: notifies the value once before it is changed, and notifies it again after it is changed, i.e., two notifications per change. NotificationIsPrior = 1; The change notification does not include notificationIsPrior and can be used in conjunction with the willChange manual notification
-
-
-
- We can also use vertical lines to make multiple choices
NSKeyValueObservingOptionOld |. NSKeyValueObservingOptionNew
So change has both new and old
- We can also use vertical lines to make multiple choices
-
- Observe object changes, callback method
observeValueForKeyPath:ofObject:change:context:
-
- KeyPath: properties of the observed object
-
- Object: Indicates the object to be observed
-
- Change: dictionary type, which holds the associated value and returns either the new value or the old value or noticationlsPrior = 1 based on the enumeration passed in options
-
- Context: The value of the context passed in to register an observer
- Can be called when the observer does not need to listen
removeObserver:forKeyPath:
Method to remove the KVO, which we need to process before the observer disappears, or crash
Dealloc actually has a bit of a problem here
Notify part addObserver: we use the selector: name: object: automatically help us to delete the observer, so we don’t have to write their own [[NSNotificationCenter defaultCenter]removeObserver:self]; To delete the observer
However, I did not find the function whether KVO will automatically delete for us in the official document
I’m going to test it is there really a normal project is there anything wrong with it
If the observer is destroyed, the observed object is not destroyed ==(for example, we are observing a property in a singleton), and then a KVO message is generated, then an exception is thrown, EXC_BAD_ACCESS
So when we use KVO, it’s best to write dealloc, remove the observer [single removeObserver:self forKeyPath:@”height”];
-
- -Blair: I’m an observer.
-
- KeyPath: properties of the observed object
Call KVO manually
KVO cannot listen inside an array element, so we need to call KVO manually
KVO is called automatically when a property is changed. If you want to control the call timing manually, or if you want to implement a KVO property call yourself, you can do so through the methods provided by KVO.
- If you want to manually call or implement KVO yourself, you need to override the following methods. This method returns YES to allow the system to call KVO automatically, NO to disallow the system to call KVO automatically
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"name"]) {
automatic = NO;// Disable automatic notification for this key. To disable KVO of this class directly, return NO;
}
else {
automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
Copy the code
- You need to override the setter method
- (void)setName:(NSString *)name {
if(name ! = _name) { [self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"]; }}Copy the code
But in general it doesn’t feel like it’s necessary to manually trigger KVO because it calls KVO’s response event twice so instead of using those two methods we just add will and did where we need to manually trigger it, okay
The essence of the KVO
KVO is based on the Runtime mechanism
At run time, create an intermediate class based on the original class, which isa subclass of the original class, and dynamically change the current object’s isa pointer to the intermediate class. And overrides the class method to return the class of the original class.
NSLog(@" Class object -%@", object_getClass(self.person));
NSLog(@" Method implementation -%p"[self.person methodForSelector:@selector(setName:)]);
NSLog(@" Metaclass object -%@", object_getClass(object_getClass(self.person)));
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
NSLog(@" Class object -%@", object_getClass(self.person));
NSLog(@" Method implementation -%p"[self.person methodForSelector:@selector(setName:)]);
NSLog(@" Metaclass object -%@", object_getClass(object_getClass(self.person)));
Copy the code
We added KVO before and after
- The set method for the class object and metaclass object to which person points has changed, as has the set method for the property to which the person is listening
- After adding KVO, ISA in Person points to
-NSKVONotifying_Person
Class object - After adding KVO,
setName:
The implementation call is: Foundation_NSSetLongLongValueAndNotify
methods
Isa-swizzling (class pointer exchange) is to take the current isa pointer of an instance object and point it to a newly constructed intermediate class, and do hook methods or other things on the newly constructed intermediate class. This will not affect other instances of the class, but only the current instance.
NSKVONotifying_Person internal implementation
- setName: The primary override method, the notification function that is called when a value is set -class: returns the original classclass-dealloc-_iskVOA to determine if this class has been dynamically subclassed by KVO - (void)setName:(int)name {
}
- (Class)class {
return [LDPerson class];
}
- (void)dealloc {
// Finishing up
}
- (BOOL)_isKVOA {
return YES;
}
Copy the code
How to call methods after ISA mixing
- Calls the listening property setting method, for example
SetAge:
Is called firstNSKVONotify_Person
The corresponding method for setting properties - Call a non-listening property setting method, such as
test
, will passNSKVONotify_Person
To find the Person class object and call the Person Test method
Why override class methods
- If the class method is not overridden, When this object calls a class method, it’s going to look for that method in its own method cache list, method list, parent class cache, method list all the way up, because a class method is a method in NSObject, and if you don’t override it it might end up returning NSKVONotifying_Person, exposing that class
The implementation of the setter is different
In the screenshot we can see that the implementation of the set method after KVO is called becomes called _NSSetIntValueAndNotify which is a C function that we don’t know what it looks like but we can test it, okay
- (void)setAge:(int)age{
_age = age;
NSLog(@"setAge:");
}
- (void)willChangeValueForKey:(NSString *)key{
[super willChangeValueForKey:key];
NSLog(@"willChangeValueForKey");
}
- (void)didChangeValueForKey:(NSString *)key{
NSLog(@"didChangeValueForKey - begin");
[super didChangeValueForKey:key];
NSLog(@"didChangeValueForKey - end");
}
Copy the code
- First call will
- And then call the original setAge
- Finally, the did method is called, notifies the listener that the property value has changed, and the listener executes the observe method
KVO section related issues
- What is the essence of KVO?
- Use Runtime’s API to dynamically generate a subclass and have the instance object’s ISA point to the new subclass
- When a property of an instance variable object is modified, the _NSSetXXXValueAndNotify function of Foundation is called in the set method of the new subclass
- willChangeValueForKey
- Call the original setter
- DidChangeValueForKey: An internal listener method that triggers the listener
- Manually trigger KVO
3. Does changing a member variable directly trigger KVO? KVO 4 will not trigger.We just print the address of the two arrays with the assignment statement and the address of the two arrays is the same, and that’s because we’re only using strong, so it’s a copy of the pointer, and everything is done to the pointer and we set KVO to one of them, we change the value of the array, and then the address of the array changes, because of KVO, Is it the method replaceObjectAtIndex?
This method returns a new array, causing the address of the original array to change, triggering the KVO listening
KVC
What is the KVC
Defined in nsKeyValuecoding.h, is an informal protocol. KVC provides a mechanism for indirectly accessing its property methods or member variables through the == string ==
NSKeyValueCoding provides KVC common access methods, namely the getter method valueForKey and the setter method setValue:forKey, and the derived keyPath method, which are common to each class. And since KVC provides the default implementation, we can also override the corresponding method to change the implementation.
Basic operation
KVC mainly operates on three types: basic data types and constants, object types, and collection types. When using KVC, the attribute name is used as the key and the value is set to assign a value to the attribute
Multiple access
In addition to assigning to the properties of the current object, you can assign to objects deeper into the object. For example, assign a value to the street property of the current object’s address property.
MyAccount setValue:@”qwe” forKeyPath@”address.street”
The ginseng nil
If you pass a nil value to a non-object, KVC will call the setNIlValueForKey method and we can override that method to avoid that
Dealing with non-objects
- When you setValue, if you want to assign a value to an object of a primitive type, you need to encapsulate the value as an NSNumber or an NSValue
- When you do valueForKey, you return an object of type id, and the basic data type is also encapsulated as NSNumber or NSValue
ValueForKey automatically encapsulates values into objects, but setValue:forKey: does not.
We have to manually convert the value type to NSNumber/NSValue in order to pass initWithBool:(BOOL)value
KVC The process of obtaining a value
We already have this problem in KVO using the ValueForKey part of the procedure that triggers the KVO listen
Now let’s look at it in detail
setValue:forKey
- The setter method is used to set the property
- If a set method is not found, the KVC mechanism will check + (Bool) accessInstanceVariablesDirectly did (direct access to the instance variables) method returns YES (the default return YES)
-
- If the override method becomes NO, the -setvalueforundefinedkey call throws an exception :(sets the value for the undefined item)
-
- If YES is returned, find the member variable and assign it directly, using _key, _isKey,key,iskey sequence, and throw an exception
Fuck a picture of Tibe from Ireland
valueForKey
- Search for getKey, key, isKey, _getKey, and _key in sequence. If a method is implemented, the value returned by the method is obtained, and the following methods will not run. If it’s a BOOL or an int or something like that, it wraps it up as an NSNumber object
- If none of the five methods is available, it will still be accessed
accessInstanceVariablesDirectly
Does the method return YES? -
- If the override method becomes NO, throw an exception
-
- Return YES and find the member variables and value them in the order _key, _isKey, key, isKey
KVC Operation scenarios
Dynamic values and Settings
The use of DYNAMIC values and Settings with KVC is the most basic use
Multivalued operation
KVC can obtain a set of values based on a given set of keys and return them in the form of a dictionary. After obtaining the dictionary, KVC can obtain the value from the dictionary through the key
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
Copy the code
Similarly, batch assignments can be done through KVC. In object call setValuesForKeysWithDictionary: method, can pass in a contains the key, the value of the dictionary in KVC all data can be carried out in accordance with the property names and dictionary of key match, and to give a value to the attribute of the User object assignment.
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
Copy the code
NSDictionary *dic = @{@"name" : @"book".@"age" : @ "66".@"sex" : @"male"};
StudentModel *model = [[StudentModel alloc] init];
[model setValuesForKeysWithDictionary:dic];
NSLog(@ "% @",model);
NSDictionary *modelDic = [model dictionaryWithValuesForKeys:@[@"name".@"age".@"studentSex"]].NSLog(@"modelDic : %@", modelDic);
Copy the code
If the model attribute does not match dic, you can override the method -(void)setValue:(id)value forUndefinedKey:(NSString *)key
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
if([key isEqualToString:@"sex"]) {
self.studentSex = (NSString*)value; }}Copy the code
Use KVC to access and modify private variables
The essence of KVC is to manipulate a list of methods and find instance variables in memory. We can use this feature to access private variables of a class.
Also if you don’t want to let the outside world use KVC methods access class member variables, can be accessInstanceVariablesDirectly attribute is set to NO
Modify the internal properties of some controls
Many UI controls are composed of internal UI controls, but Apple does not provide an API to access these controls, so we cannot normally access and modify the style of these Spaces. KVC solves this problem in most cases