What is the KVO

Official document Key-value observing is a mechanism that allows objects to be notified when specified properties of other objects change.

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

We usually pass NULL in the last argument context, for example

[self.person addObserver:self forKeyPath:@"nick" options:NSKeyValueObservingOptionNew context:NULL];
Copy the code

Context

A safer and more extensible approach is to use the context to ensure notifications you receive are destined for your A more secure and extensible approach is to use context to ensure that notifications received are addressed to observers and not to the superclass. Listing 1 Creating context for Pointers

static void *PersonAccountBalanceContext = &PersonAccountBalanceContext;
static void *PersonAccountInterestRateContext = &PersonAccountInterestRateContext;
Copy the code

Listing 2 Registering the inspector as an observer of the balance and interestRate properties

- (void)registerAsObserverForAccount:(Account*)account {
    [account addObserver:self
              forKeyPath:@"balance"
                 options:(NSKeyValueObservingOptionNew |
                          NSKeyValueObservingOptionOld)
                 context:PersonAccountBalanceContext];
 
    [account addObserver:self
              forKeyPath:@"interestRate"
                 options:(NSKeyValueObservingOptionNew |
                          NSKeyValueObservingOptionOld)
                  context:PersonAccountInterestRateContext];
}
Copy the code

Remove observer

An observer does not automatically remove itself when deallocated. The observed object continues to send notifications, oblivious to the state of the observer. However, a change notification, like any other message, sent to a released object, triggers a memory access exception. You therefore ensure that observers remove themselves before disappearing from The observer does not automatically remove itself when released. The observed object continues to send notifications, ignoring the status of the observer. However, like any other message, a change notification sent to a released object triggers a memory access exception. Therefore, you can ensure that the observer removes itself before it disappears from memory)

Automatic Change Notification Automatically

One to many: for example, the progress of downloading a video, writte and Total, will change

+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
    
    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
    if ([key isEqualToString:@"downloadProgress"]) {
        NSArray *affectingKeys = @[@"totalData".@"writtenData"];
        keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
    }
    return keyPaths;
}
Copy the code

Manual Change Notification Manual Change

+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key{
    return NO;
}
Copy the code
- (void)setNick:(NSString *)nick{
    [self willChangeValueForKey:@"nick"];
    _nick = nick;
    [self didChangeValueForKey:@"nick"];
}
Copy the code

Observation of mutable arrays

The protocol defines three different proxy methods for collection object access. each with a key and a key path variant: mutableArrayValueForKey: and mutableArrayValueForKeyPath:These return a proxy object that behaves like an NSMutableArray object. mutableSetValueForKey: and mutableSetValueForKeyPath:These return a proxy object that behaves like an NSMutableSet object. mutableOrderedSetValueForKey: and mutableOrderedSetValueForKeyPath:These return a proxy object that behaves like an NSMutableOrderedSet object.

[[self.person mutableArrayValueForKey:@"dateArray"] addObject:@ "1"];
Copy the code

Underlying principles of KVO

When adding an observer:When removing observers:We found that after adding KVO observers,self.personPointing to aNSKVONotifying_PersonWhen removing the observer, ISA refers back (but createdNSKVONotifying_PersonThe class was not destroyed. We can guess that this is dynamically generated at run time. Then thisNSKVONotifying_PersonandLGPersonWhat does it matter?

#pragmaMark - Traverses classes and subclasses
- (void)printClasses:(Class)cls{
    // Total number of registered classes
    int count = objc_getClassList(NULL.0);
    // Create an array containing the given object
    NSMutableArray *mArray = [NSMutableArray arrayWithObject:cls];
    // Get all registered classes
    Class* classes = (Class*)malloc(sizeof(Class)*count);
    objc_getClassList(classes, count);
    for (int i = 0; i<count; i++) {
        if (cls == class_getSuperclass(classes[i])) {
            [mArray addObject:classes[i]];
        }
    }
    free(classes);
    NSLog(@"classes = %@", mArray);
}
Copy the code

We print the subclass of the current Person before and after adding observers to Person;

    [self printClasses:[Person class]];
    [self.person addObserver:self forKeyPath:@"nickName" options:(NSKeyValueObservingOptionNew) context:NULL];
    [self printClasses:[Person class]].Copy the code

It can be concluded that: currentNSKVONotifying_PersonisPersonA subclass of

What does NSKVONotifying_Person contain

There are only a few key things for a class: properties, methods, and protocols

#pragmaMark-traversal method-ivar-property
- (void)printClassAllMethod:(Class)cls{
    unsigned int count = 0;
    Method *methodList = class_copyMethodList(cls, &count);
    for (int i = 0; i<count; i++) {
        Method method = methodList[i];
        SEL sel = method_getName(method);
        IMP imp = class_getMethodImplementation(cls, sel);
        NSLog(@"%@-%p".NSStringFromSelector(sel),imp);
    }
    free(methodList);
}
Copy the code
[self printClassAllMethod:objc_getClass("NSKVONotifying_Person")];
Copy the code

Overrides these four methods:setNickNameclassdealloc_isKVOA

Is it a member variable or a Property ivar VS Property that KVO looks at

Although NskVonoJustify _Person overwrites the set method, it leaves us wondering whether the observer is observing a member variable or a property. Continue to verify

@interface Person : NSObject{
    @public
    NSString *name;
}
@property (nonatomic.copy) NSString *nickName;

@end
Copy the code
[self.person addObserver:self forKeyPath:@"nickName" options:(NSKeyValueObservingOptionNew) context:NULL];
[self.person addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
Copy the code

KVO callback

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey.id> *)change context:(void *)context{
    NSLog(@ "% @",change);
}
Copy the code

Printout:Conclusion: No member variables are observed.

Setter method listening

We know from the above that KVO actually looks at the set method, what the set method does. Debug using LLDBA hit occurs when a change is observedbtthetouchesBegintosetNickNameBetween the system calls three methods: 1.-[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:maybeNewValuesDict:usingBlock:] 2.-[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] 3._NSSetObjectValueAndNotifyClick through the assembly code in the call stack to get a rough idea of the flow: when we start listening, one is generatedNSKVONotifying_Person, four methods are generated simultaneously. When we assign a value to a set, the set is actuallyNSKVONotifying_PersonSet method, which is now processed in method 3willChangeValueForKey:anddidChangeValueForKey:The callback notification comes after the modification.