iOS中kvo相关应用
2020-04-18 本文已影响0人
楼上那只猫
什么是kvo?
一句话:kvo是一种机制,可以允许一个对象去监控另一个对象的属性变化,并且在属性变化时得到通知。
Key-value observing is a mechanism that allows objects to be notified of changes to specified properties of other objects.
如何取消系统自动发送的通知,而手动控制?
假设Person类的firstName属性被监控,当其发生变化的时候,我们想手动控制什么时候发出通知,或者在发出通知前做其它操作,那么需要在Person中重写如下方法。
- (void)setFirstName:(NSString *)firstName {
// 必须手动调用
[self willChangeValueForKey:@"firstName"];
_firstName = firstName;
NSLog(@"xxxx");
// 必须手动调用
[self didChangeValueForKey:@"firstName"];
}
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([key isEqualToString:@"firstName"]) {
return NO;
}
return YES;
}
如果被监控的属性的变化受到多个其它属性的影响,如何实现?
如果Person中的fullName被监控,但是fullName的值受到firstName和lastName的影响。
- (NSString *)name {
return [NSString stringWithFormat:@"%@%@", _firstName, _lastName];
}
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
NSSet * keypath = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"name"]) {
// 指定影响name变化的属性有哪些
NSArray * effectKeys = @[@"firstName", @"lastName"];
keypath = [keypath setByAddingObjectsFromArray:effectKeys];
}
return keypath;
}
如何处理一对多的关系?
假设,有一个Employee类,有一个属性salary。
控制器中有一个属性emps,数组,包含的是每个Employee类的实例.
控制器中还有一个表示总的salary的属性total。
@interface Employee : NSObject
@property(nonatomic, assign) NSInteger salary;
@end
@interface ViewController ()
@property(nonatomic, copy) NSArray * emps;
@property(nonatomic, strong) NSNumber * total;
@end
Employee * e1 = [[Employee alloc] init];
e1.salary = 100;
Employee * e2 = [[Employee alloc] init];
e2.salary = 100;
Employee * e3 = [[Employee alloc] init];
e3.salary = 100;
[self setValue:@[e1, e2, e3] forKey:@"emps"];
NSIndexSet * set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.emps.count)];
//这里注册为emps中的每个对象的变化都会发出通知
[self.emps addObserver:self toObjectsAtIndexes:set forKeyPath:@"salary" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:@"total" options:NSKeyValueObservingOptionNew context:nil];
e3.salary = 300;
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"change %@", change);
if ([keyPath isEqualToString:@"salary"]) {
[self updateTotal];
} else {
NSLog(@"---%ld", [self.total integerValue]);
}
}
- (void)updateTotal {
//每次salary变化都会重新计算总和
NSNumber * value = [self valueForKeyPath:@"emps.@sum.salary"];
[self setValue:value forKey:@"total"];
}