KVO是一种观察者模式,需要观察者&被观察者的对象属性,移除观察者1.使用(参数的意义,哪些地方使用了KVO)
2. 开启方式:手动和自动
3. 原理解析:系统做了哪些东西;是否监听地址变化?
4. 容器类监听使用
5. 多级路径属性管理
6. KVO为什么是基于KVC的:KVC是KVO的一个入口而已
使用场景(哪些地方使用了KVO)1.NSOperation
2.NSoperationQueue
3.RAC
开启方式:手动&自动1.自动:只需要实现响应的
2.手动触发:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
{
return NO;
}上面的代码返回NO之后,你还需要自己手动触发KVO:此时willchange & didchange 缺一不可
Person *p = [Person new];
[p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
[p willChangeValueForKey:@"name"];
p.name = @"new_name";
[p didChangeValueForKey:@"name"];手动触发KVO:可以自己控制在某些条件下进行KVO
原理解析:利用Runtime,生成一个类的子类,并生成子类对象,并替换了原来对象的isa指针,并且重写了set方法
1. KVO的本质是set方法,只有调用了set方法才会触发KVO我们写一个Person类,设置age & name两个属性:
#import
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
NS_ASSUME_NONNULL_END正常的Person对象的布局
当我们添加addObserver之后,runtime会对Person对象重新布局:重新布局后
从上面的关系图,我们可以看到runtime为我们生产NSKVONotifying_Person类,继承自Person;并修改了Person实例对象的isa指针,指向新的类。而且重写set方法:
- (void)setName:(NSString *)name
{
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}
容器类的KVO --NSArray, NSDictionary
初始化一个容器属性:
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, strong) NSArray *ecoArr;
@end[p.ecoArr addObject:@"LI"];这样调用可以出发KVO吗?
A: 不会,因为这样没有调用属性ecoArr的set方法。
[[p.ecoArr mutableArrayValueForKeyPath:@"eocArr"] addObject:@"li"];这样就就可以对这个容器进行插入KVO观察。
typedef NS_ENUM(NSUInteger, NSKeyValueChange) {
NSKeyValueChangeSetting = 1,
NSKeyValueChangeInsertion = 2,
NSKeyValueChangeRemoval = 3,
NSKeyValueChangeReplacement = 4,
};
对于容器类的KVO我们还可以监听容器属性是发生的插入、移除、替换操作。
多级路径属性管理
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
NSSet *keySet = [super keyPathsForValuesAffectingValueForKey:key];
NSSet *a = [NSSet setWithObject:@"_person.age"];
keySet = [keySet setByAddingObjectsFromSet:a];
return keySet;
}
这样可以将多及路径进行观测,这里注意object写法是要下划线_person