1, the introduction of
KVO (Key-value Observe) is a design mode for monitoring attribute changes based on KVC. If KVO is set for a property of a class, when that property changes, the listening method is triggered to know that the property has changed. If this one change will affect other multiple attribute changes, we will also often rewrite this attribute set method, used to listen to his change, but if not the attributes of a class, we can’t do the rewrite the set method, the KVO can play, actually KVO is essentially rewrite set method, The entire process depends on the Runtime.
2, use,
1) Set the listener
// Initialize a test class that declares a property: nameStr _kvoTest = [[KVOTestModel alloc] init]; / / to attribute nameStr set up to monitor, types, back to the tone value is different, here is the new value (NSKeyValueObservingOptionNew) monitoring, can also monitor other changes, Specific see enumerated [_kvoTest addObserver: self forKeyPath: @ "nameStr" options: NSKeyValueObservingOptionNew context: nil];Copy the code
2) Implement the listening callback method
// When listening properties change through KVC, Implement the following method - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object Change (NSDictionary<NSKeyValueChangeKey,id> *)change context (void *)context {// _kvoTest if ([keyPath isEqualToString:@"nameStr"]) {// We print the new value NSLog(@"change: %@",[change objectForKey:NSKeyValueChangeNewKey]); }}Copy the code
3) Special care should be taken to remove observers when using KVO, No crash happens when the class is going to be released (Terminating app due to uncaught exception ‘NSInternalInconsistencyException, reason: ‘An instance *** of class *** was deallocated while key value observers were still registered with it.)
- (void)dealloc {
[_kvoTest removeObserver:self forKeyPath:@"nameStr"];
}
Copy the code
One thing about this, by the way, is that iOS11 will not crash if you don’t call the above method, but iOS10 and below will crash. There is no relevant explanation found in the official document (after iOS9, NSNotification has not been removed, which may be the trend).
3. Implementation principle
KVO is implemented according to iOS Runtime. When listening to a property of an object (_kvoTest), KVO subclasses that object and overwrites the set method of the property we are listening to (keyPath). The implementation might look something like this.
- (void)setNameStr:(NSString *)nameStr {
[self willChangeValueForKey:@"nameStr"];
[super setValue:nameStr forKey:@"nameStr"];
[self didChangeValueForKey:@"nameStr"];
}
Copy the code
DidChangeValueForKey: After the method is executed, KVO returns a different dictionary based on the option at registration time
As mentioned above, KVO is based on KVC. As you can see, if a property is not changed through KVC, then the set method will not be executed and KVO will not be able to listen for property changes.
4. Be more specific (Principle)
We all say that it is implemented through Runtime, because this operation is dynamically added at runtime. You can read more articles about Runtime, know the principle, use it more easily, and have more ideas.
When an object is observed, the KVO mechanism dynamically creates a new class named NSKVONotifying_ object name, which inherits from the class of the target object, and KVO overrides the set method of the observed property for the NSKVONotifying_ object name. In this process, the isa pointer of the observed object is changed from pointing to the original object by KVO mechanism to pointing to the newly created subclass NSKVONotifying_ object name class to realize the monitoring of the change of the current class attribute value, which is the “black magic” mentioned above. Create a new class named “NSKVONotifying_ Object name”, and find that iOS10 and below will crash when running the code to register KVO, iOS11 console print a warning:
[general] KVO failed to allocate class pair for name NSKVONotifying_KVOTestModel,
automatic key-value observing will not work for this class
Copy the code
A class with the same name exists and KVO listening cannot be performed
5, extension,
Many of you have used YYKit, a powerful open source class library. There is an extension class NSObject+YYAddForKVO, which encapsulates KVO and makes it much easier to use. It dynamically adds attributes to the class at runtime, storing all keyPath and callbacks. Three methods:
/**
Registers a block to receive KVO notifications for the specified key-path
relative to the receiver.
@discussion The block and block captured objects are retained. Call
`removeObserverBlocksForKeyPath:` or `removeObserverBlocks` to release.
@param keyPath The key path, relative to the receiver, of the property to
observe. This value must not be nil.
@param block The block to register for KVO notifications.
*/
- (void)addObserverBlockForKeyPath:(NSString*)keyPath block:(void (^)(id _Nonnull obj, _Nullable id oldVal, _Nullable id newVal))block;
/**
Stops all blocks (associated by `addObserverBlockForKeyPath:block:`) from
receiving change notifications for the property specified by a given key-path
relative to the receiver, and release these blocks.
@param keyPath A key-path, relative to the receiver, for which blocks is
registered to receive KVO change notifications.
*/
- (void)removeObserverBlocksForKeyPath:(NSString*)keyPath;
/**
Stops all blocks (associated by `addObserverBlockForKeyPath:block:`) from
receiving change notifications, and release these blocks.
*/
- (void)removeObserverBlocks;
Copy the code
The above is the implementation principle of iOS KVO and the use of detailed content, I hope to help you
The original address