KVO

1、KVO是什么?

KVO(Key Value Observing),是观察者模式在Foundation中的实现

2、KVO 用法?

主要有三个方法:
1、注册

1
2
3
4
5
6
7
//keyPath就是要观察的属性值
//options给你观察键值变化的选择
//context方便传输你需要的数据
-(void)addObserver:(NSObject *)anObserver
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context

2、实现监听

1
2
3
4
5
//change里存储了一些变化的数据,比如变化前的数据,变化后的数据;如果注册时context不为空,这里context就能接收到。
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context

3、移除

1
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

3、KVO原理?

1、当一个object有观察者时,动态创建这个object的类的子类。一般就是 NSKVONotifying_类名 ,同时会重写这个类的 -class方法,返回之前的类,来隐藏实现细节。通过 object_getClass() 这个方法来调用可以进行验证,因为这个方法是返回isa指针。
2、对于每个被观察的property,重写其set方法
3、在重写的set方法中调用- willChangeValueForKey:和- didChangeValueForKey:通知观察者
4、当一个property没有观察者时,删除重写的方法
5、当没有observer观察任何一个property时,删除动态创建的子类

KVO会重写这些方法:

1
2
3
4
- setXXX:最主要的重写方法,set值时调用通知函数
- class 隐藏自己必备啊,返回原来类的class
- dealloc 做清理工作
- _isKVOA 这就是内部使用的标示了,判断这个类有没被KVO动态生成子类

4、在什么时机创建的这个子类?

1、当类A的对象第一次被观察的时候,系统会在运行期动态创建类A的派生类。我们称为B。
2、在派生类B中重写类A的setter方法,B类在被重写的setter方法中实现通知机制。
3、类B重写会 class方法,将自己伪装成类A。类B还会重写dealloc方法释放资源。
4、系统将所有指向类A对象的isa指针指向类B的对象。

5、一个类里有特别多属性都需要观察怎么办?

1
2
//返回影响某个key的所有字段
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key;

6、类里有个 数组 NSArray的属性 可以观察内容变化吗?

使用mutableArrayValueForKey: 来修改属性内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@interface demo : NSObject
@property (nonatomic,strong) NSMutableArray* arr;
@end
@implementation demo
-(id)init{
if (self == [super init]){
_arr = [NSMutableArray new];
[self addObserver:self forKeyPath:@"arr" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
}
return self;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
NSLog(@"%@",change);
}
-(void)dealloc{
[self removeObserver:self forKeyPath:@"arr"]; //一定要在dealloc里面移除观察
}
-(void)addItem{
//不会触发KVO
[_arr addObject:@"1"];
}
-(void)addItemObserver{
//会触发KVO
[[self mutableArrayValueForKey:@"arr"] addObject:@"1"];
}
-(void)removeItemObserver{
//会触发KVO
[[self mutableArrayValueForKey:@"arr"] removeLastObject];
}
@end

6、参考资料

objc kvo简单探索
深入理解KVO机制