There are several main methods of KVO

  • Add observer
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
Copy the code
  • Remove observer
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
Copy the code
  • To observe the callback
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;
Copy the code

Parameter description: Observer: indicates the observer. Generally, self is transmitted. KeyPath: path that passes in the property variables of the observed object (the member variables do not take effect because they do not have setter methods). Options: enumeration, a total of four values, the most commonly used for NSKeyValueObservingOptionNew.

NSKeyValueObservingOptionNew new value NSKeyValueObservingOptionOld NSKeyValueObservingOptionInitial old values The initial value (in the registered monitoring service called when a trigger method) NSKeyValueObservingOptionPrior respectively before and after the change in value trigger method (i.e., a modified trigger twice)Copy the code

Context: the context is labeled so that it can be distinguished in the observation callback to prevent inheritance. Object can do a similar thing, but object has to iterate over a lot of things, and context has better performance. Pass NULL if you don’t want to use it. Object: indicates the observed object. This prints out as . Change: something that has changed.

####KVO basic usage 1. Create a class, name it Person, and add member and attribute variables, respectively.

@interface Person : NSObject{
    NSString *nickName;
}
@property(strong,nonatomic)NSString * name;
@property(assign,nonatomic)int age;
@property(strong,nonatomic)NSMutableArray * cars;
@end
Copy the code

2. In the controller, create the Person object and add an observer to the object.

@property(strong,nonatomic)Person *  p;

self.p = [[Person alloc]init];
[self.p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
[self.p addObserver:self forKeyPath:@"age" options:(NSKeyValueObservingOptionNew) context:NULL];
Copy the code

3. Add an observation callback

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

4. Remove observer (mandatory), failure to remove observer may cause crash.

-(void)dealloc{
    [self.p removeObserver:self forKeyPath:@"name"];
}
Copy the code

5. Trigger listening (example)

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.p.name = @"Yusensen";
    self.p.age ++;
    [[self.p mutableArrayValueForKey:@"cars"] addObject:@"Aston Martin."];
}
Copy the code

Some operations

  • Automatic observation Observation object setting Automatic observation (not so much automatic, but can be restricted) the following method onlykeyIs equal to thename“Will trigger the observation callback.
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
    if ([key isEqualToString:@"name"]) {
        return YES;
    }
    return NO;
}
Copy the code
  • Manual observation When restricted by the above method, you can restore the observation by manual observation
-(void)setAge:(int)age{
    [self willChangeValueForKey:@"age"];
    _age = age;
    [self didChangeValueForKey:@"age"];
}
Copy the code
  • Linkage Observation When you need to observe an object affected by multiple factors, you can perform linkage operations.
// 1. Create 3 attributes @property(assign,nonatomic)doubledone; @property(assign,nonatomic)double total; // total @property(strong,nonatomic)NSString *downloadProgress; / / download progress / / 2. The attribute + (NSSet < > nsstrings * *) keyPathsForValuesAffectingValueForKey: (nsstrings *) key {NSSet * keyPaths = [super keyPathsForValuesAffectingValueForKey:key];if ([key isEqualToString:@"downloadProgress"]) {
        NSArray *array = @[@"done"The @"total"];
        keyPaths = [keyPaths setByAddingObjectsFromArray:array];
    }
    returnkeyPaths; } // 3. Add listener [self. addObserver:selfforKeyPath:@"downloadProgress"options:(NSKeyValueObservingOptionNew) context:NULL]; // 3. (void)touch began :(NSSet< touch *> *)touches withEvent:(UIEvent *)event{self.p.done +=10; } //downloadProgress lazy load -(NSString *)downloadProgress{if (self.done == 0) {
        _done = 10;
    }
    if (self.total == 0) {
        _total = 100;
    }
    return [NSString stringWithFormat:@"%f", 1.0 f * self. Done/self. Total]; }Copy the code
  • The collection type (NSMutableArray, NSMutableSet).
-(void)touchesBegan:(NSSet<UITouch *> *) Touches withEvent:(UIEvent *) Event {// Self.p.cirdobject :@lamborghini]; // In KVO, the set type (Array,set) operation. // The process of evaluation and assignment is different from that of normal cases, which requires KVC method. [[self.p mutableArrayValueForKey:@"cars"] addObject:@lamborghini];
}
Copy the code

extension

Since KVO is not open source, in order to understand the internal implementation principle of KVO, we try to explore the underlying principle of KVO