Implementation of KVO key value observation and kvo key value observation
1. Introduction to KVO
KVO is an implementation of Objective-C's observer design pattern. It provides A mechanism to specify an object to be observed (such as class ), when a property in an object changes, the object will receive a notification and handle it accordingly. For projects under the MVC design architecture, the KVO mechanism is suitable for communication between the mode model and the view. For example, in the Code, create Attribute Data in model Class A and create an observer in the Controller. Once the attribute data changes, the observer receives A notification, the controller uses the callback method to update View B through KVO;
2. Implementation Principle
The implementation of KVO depends on the powerful runtime of Objective-C. The underlying implementation of KVO is to listen to the setter method. When you observe object A, the KVO dynamic mechanism will dynamically create A subclass of Class A and rewrite the setter method of the parent class for this new subclass. The setter method is then responsible for notifying the observed object attribute changes.
3. In-depth understanding
Apple uses isa-swizzling to implement KVO. When we observe object A (that is, when we call object A to register the observer ), the KVO mechanism dynamically creates A new subclass of A: NSKVONotifying_A, which inherits from the class of object. KVO will rewrite the setter method of the parent class for NSKVONotifying_A. The setter method will notify all the changes to the object attribute values before or after the original setter method is called.
Internal implementation of the addobserver method:
In this method, the isa pointer of the observed object is directed from Class A to Class A, and the KVO mechanism is changed to NSKVONotifying_A, A subclass of Class A, to monitor changes to the attribute values of the current class.
Isa pointer: each object has an isa pointer pointing to the class of this object. It tells the Runtime system what the class of this object is, so when the object is registered as an observer, the isa pointer will point to the new subclass, and the object will become the new subclass object. Therefore, when this object calls setter, it will call the rewritten setter to activate the key-value notification mechanism.
Analysis of the setter method of subclass:
KVO's key value observation notification relies on two NSObject Methods: willChangeValueForKey didChangeValueForKey, which are called before and after the access value. Observed attribute occurrenceBefore change, WillChangeValueForKey: called to notify the system that the keyPath attribute value is about to change. WhenAfter a change occurs, DidChangeValueForKey: called to notify the system that the keyPath attribute value has changed;After, ObserveValueForKey: ofObject: change: context: will also be called. In addition, the setter Method for rewriting observation attributes is implemented at runtime rather than during compilation.
4. Implement KVO by yourself
First create a NSObject class extension ,. in the hfile, you can customize one method. Because the target is to customize KVO, you only need to add a prefix before the method name of the system to add the observer, and the parameter value remains unchanged. The method is as follows:
-(Void) CC_addObserver :( NSObject *) observer forKeyPath :( NSString *) keyPath options :( NSKeyValueObservingOptions) options context :( nullablevoid *) context {/* 1. customize a ckvo_xxx subclass 2. override the setter of the parent class and restore the subclass internally to notify the observer 3. modify the self pointer to the newly created NSCCKVO_XXX subclass * // dynamically generate a class NSString * oldClassName = NSStringFromClass ([selfclass]); NSString * newClassName = [@ "NSCCKVO _" stringByAppendingString: oldClassName]; constchar * name = [newClassNameU TF8String]; // define a Class // parameter 1: The inherited Class parameter 2: Class name ccClass = objc_allocateClassPair ([selfclass], name, 0 ); // Add the setter Method to the subclass. Take setName as an example: class_addMethod (ccClass, @ selector (setName :), (IMP) setName, "v @:@"); // register this class objc_registerClassPair (ccClass); // modify the isa pointer object_setClass (self, ccClass) of self; // bind the observer to the self object, bind the observer to the current object objc_setAssociatedObject (self, (_ bridgeconstvoid *) @ "objc", observer, OBJC_ASSOCIATION_RETAIN_NONATO MIC);} voidsetName (idself, SEL_cmd, NSString * newName) {// call the sett method Class superClass = class_getSuperclass ([selfclass]) of the parent Class; // change the isa pointer. As the parent class, call the set Method object_setClass (self, superClass); objc_msgSend (self, @ selector (setName :), newName); // take out the observer idobserver = objc_getAssociatedObject (self, (_ bridgeconstvoid *) @ "objc"); // notify the observer objc_msgSend (observer, @ selector (observeValueForKeyPath: ofObject: change: context :), @ "name", self, @ {@ "new": newName}, nil); // change the subclass type object_setClass (self, [selfclass]);}
Then import the class extension header file of NSObject in ViewController.
Person * person = [[Personalloc] init]; _ person = person; // [person addObserver: self forKeyPath: @ "name" options: NSKeyValueObservingOptionNew context: nil]; // This method is the one implemented by myself just now [personCC_addObserver: selfforKeyPath: @ "name" options: NSKeyValueObservingOptionNewcontext: nil];-(void) observeValueForKeyPath :( NSString *) keyPath ofObject :( id) object change :( NSDictionary *) change context :( void *) context {NSLog (@ "% @ value changed to % @", object, keyPath, change [@ "new"]);}
Here, you can implement KVO on your own. The observer observes the attributes. The callback method of KVO is executed only when the attribute value of KVO is changed. For example, whether the setter method is executed or whether KVC value is used. If the value assignment does not pass the setter method or KVC, but directly modifies the member variables corresponding to the attribute. For example, if only _ name = @ "newName" is called, The kvo mechanism is not triggered, the callback method will not be called. Therefore, the premise of using the KVO mechanism is to follow the KVO attribute setting method to change the attribute value.