The realization principle of KVO and response programming

About KVO, has written two articles before

KVO set navigationBar gradient transparent effect

Observer pattern (two) —KVO

However, on the KVO internal implementation, but I did not in-depth study, then we look at what happened inside the KVO:

I first create a Person class, within the class has a property name:

#import < Foundation/Foundation.h> @interface Person: NSObject @property (nonatomic, copy) NSString *name; @end

Then I let the controller as an observer to observe the changes in the name property:

- (void) viewDidLoad [self.person addObserver:self forKeyPath:@ {[super viewDidLoad]; name options:0 context:nil];} - (void) touchesBegan: (NSSet< UITouch *> touches withEvent: (* *) UIEvent event static int) {number = 0; self.person.name = [NSString stringWithFormat:@ "% @, @ (number + +)]} - (; void) observeValueForKeyPath: (NSString *) keyPath ofObject: (ID) object change: (NSDictionary< NSKeyValueChangeKey, id> *) change context: (void * context) {}

I hit a breakpoint in the twenty-fourth line, look at the person isa pointer, found that this time, the ISA pointer is pointing to the Person

The realization principle of KVO and response programming
isa pointer to Person

But skip the breakpoint to the 24 line and find that the ISA pointer points to NSKVONotifying_Person.

The realization principle of KVO and response programming
isa pointer to NSKVONotifying_Person

NSKVONotifying_Person is a subclass of Person

- (void) addObserver: (NSObject *) observer forKeyPath: (NSString *) keyPath options: (NSKeyValueObservingOptions) options context: (nullable void *) context;

Method to dynamically generate a subclass of ISA, the pointer to this subclass, so, when calling the set method, the set method will go subclass, set subclass methods, first super superclass methods, so it does not affect the set method in the Person class to rewrite.

Why is the set method, because the essence of KVO is to override the set method, using runtime call mechanism (void) (nullable – observeValueForKeyPath: NSString * keyPath ofObject:) (nullable ID) object change: (nullable NSDictionary< NSKeyValueChangeKey, id> change * context: (nullable) void * context); and then realize the method, in this method in the realization of the logic processing. Each call a set method, this method will come in, this is the essence of KVO.

Based on the above idea, we have come up with a KVO method:

  • First, create a NSObject category,
Classification of The realization principle of KVO and response programming
NSObject
  • Write a classification method and system, add a prefix to prevent duplicate:
- (void) yf_addObserver: (NSObject *_Nullable) observer forKeyPath: (NSString *_Nullable) options: keyPath (NSKeyValueObservingOptions) options context: (nullable void *);
  • This method is then implemented: first, the observer keyPath is set to an attribute that is used by the following function:
Objc_setAssociatedObject (self, @ keyPath), keyPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  • Dynamically generate a subclass:
Get the current class 1 / * * / Class Klass = [self class]; NSString *className = NSStringFromClass (Klass); / * 2 dynamically generate a subclass of class subclass 2.1 / * * / NSString *subclassName [@ = "YFKVO" stringByAppendingString:className]; / * 2.2 dynamically generate a subclass of subclass / Class = objc_allocateClassPair (Klass, [subclassName UTF8String], 0); objc_registerClassPair (subclass); object_setClass (self, subclass);
  • Add set method for this subclass:
3 / for a subclass of set method to add 3.1 / * * / NSString *methodName method to obtain the name = "set%@:" [NSString stringWithFormat:@, keyPath.localizedCapitalizedString] SEL; sel = NSSelectorFromString (methodName); / * * / class_addMethod 3.2 dynamically add methods (subclass, SEL, setValue (IMP), "[email protected]: @");
  • Send set method to object:
4 to send set / * * / objc_msgSend method (self, SEL); / * 5 save observation, so that the following function call / objc_setAssociatedObject (self, observer, observer, @ OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  • Realization of setValue function:
Void setValue (ID self, SEL _cmd, ID value) {/ * 1 first pointer to super 1.1 class / * * / Class now save subclass = [self class]; object_setClass (self [self superclass]); / * 2 call the parent class set / NSString *keyPath = objc_getAssociatedObject (self @ keyPath) NSString; *methodName [NSString stringWithFormat:@ = "set%@", keyPath.localizedCapitalizedString] SEL; sel = NSSelectorFromString (methodName); objc_msgSend (self, SEL, value); / * 3 isa pointer to subclasses / object_setClass (self, subclass); / * 4 to send a message to the observer to obtain 4.1 * / / * * / observer ID observer (self, objc_getAssociatedObject = @ "Observer"); objc_msgSend (observer, @selector (observeValueF OrKeyPath:ofObject:change:context:), keyPath, self, nil, Nil);}
  • To note here is twofold: first to call the set method of the superclass, so when I use KVO, set method of this class will not affect the rewrite. In the set method call the parent class, our strategy is to take the ISA pointer to the parent class, but in the end we function. You need to isa the pointer again pointing the subclass, so, I can guarantee that I is a custom set method will be invoked.
The realization principle of KVO and response programming
using custom KVO run results
  • Even rewrite the Person class set method does not affect:
#import "Person.h" Person @implementation (void) setName: (NSString *) name {_name = [name stringByAppendingString:@ "override the set method that does not affect KVO implementation";}
The realization principle of KVO and response programming
override the Person class set method does not affect the implementation of KVO
In fact, the realization process of KVO is a kind of thought of response programming