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
####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 only
key
Is 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