OC观察者模式之KVO
2019-01-15 本文已影响10人
芝麻酱的简书
KVO:
- KVO是key-value observing的缩写
- KVO是OC对观察者设计模式的又一实现
- 苹果使用了isa混写(isa-swizzling)来实现KVO
1.KVO常用API
# observer是观察者,必须实现
# options:KVO的一些属性配置:
NSKeyValueObservingOptionNew:change字典包括改变后的值
NSKeyValueObservingOptionOld:change字典包括改变前的值
NSKeyValueObservingOptionInitial:注册后立刻触发KVO通知
NSKeyValueObservingOptionPrior:值改变前是否也要通知(这个key决定了是否在改变前改变后通知两次)
- (void)addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
2.禁用KVO
1). 禁用部分属性的KVO
-
首先,需要手动实现属性的
setter
方法,并在设置操作的前后分别调用willChangeValueForKey:
和didChangeValueForKey方法
,这两个方法用于通知系统该 key 的属性值即将和已经变更了; -
其次,要实现类方法
automaticallyNotifiesObserversForKey
,并在其中设置对该 key 不自动发送通知(返回 NO 即可)。这里要注意,对其它非手动实现的 key,要转交给 super 来处理。
- (void)setName:(NSString *)name {
[self willChangeValueForKey: @"name"];
_name = name;
[self didChangeValueForKey: @"name"];
}
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([key isEqualToString: @"name"]) {
return NO;
}
return [super automaticallyNotifiesObserversForKey: key];
}
2). 禁用所有属性的KVO
如果需要禁用该类KVO的话直接automaticallyNotifiesObserversForKey
返回NO,实现属性的setter
方法,不进行调用willChangeValueForKey:
和 didChangeValueForKey
方法。
- (void)setName:(NSString *)name {
_name = name;
}
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
return NO;
}
3.isa混写技术在KVO中实现

派生类中实现:
- (void)setValue:(id)obj {
[self willChangeValueForKey: @"keypath"];
[super setValue: obj]
# didChange方法触发监听回调
[self didChangeValueForKey: @"keypath"];
}
当我们注册一个对象的观察者后,实际上是调用了系统的addObserver:ForKey:path:
方法,系统会为我们在运行时动态的创建一个NSKVONotifying_被观察类的名字
这样一个类,并将原来被观察的类的isa指针指向新创建的类(这一步就是isa混写技术的标志)。NSKVONotifying_被观察类的名字
该类为原被观察类的子类,并重写了setter方法,来达到负责通知所有观察者这一目的。
Objective-C 在发送消息的时候,会通过 isa 指针找到当前对象所属的类对象。而类对象中保存着当前对象的实例方法,因此在向此对象发送消息时候,实际上是发送到了派生类对象的方法。由于编译器对派生类的方法进行了 override,并添加了通知代码,因此会向注册的对象发送通知。注意派生类只重写注册了观察者的属性方法。
4.KVO什么时候会生效:
- 使用
setter
方法改变值会触发KVO - 使用
setValue: forKey:
改变值会触发KVO - 成员变量直接修改需要手动添加
willChange
和didChange
方法,KVO才会触发