观察者模式
观察者模式记录~~~
什么是观察者模式
- 观察者模式定义:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
- iOS中实现观察者模式:Notification、KVO。
Notification – 通知
现有对象A和B,A对B的变化感兴趣,就注册为B的观察者,当B发生变化时通知A,告知B发生了变化。
- 对于感兴趣的A来说,在A这里定义通知,也就是注册观察者(A就是观察者,定义怎么观察的以及观察到了会做些什么)
1 | //注册观察者 |
- 对于变化源B来说,在B这里发出通知
1 | //创建通知对象 |
- 在dealloc中移除观察者
1
2
3
4- (void)dealloc {
//根据name和object删除对应的观察者,如果object设置为nil,则删除所有name匹配的观察者
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"tongzhi" object:nil];
}
KVO – Key Value Observing
KVO,即键值观察,它是观察着模式的一种衍生。其基本思想是,对目标对象的某属性添加观察,当该属性发生变化时,会自动通知观察者。相比于NotificationCenter的post通知来说简单了许多。
首先,给目标对象的属性添加观察
1
2
3
4- (void)addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld //能够记录旧值和新值
context:nil其次,实现下面方法来接收通知(当被观察的属性发生变化时,观察者立马会得到通知)
1
2
3
4
5
6
7
8
9
10- (void)observeValueForKeyPath:(nullable NSString *)keyPath //目标属性,需判断是否和自己所观察的属性一致
ofObject:(nullable id)object //目标对象,需判断是否和自己所观察的对象一致
change:(nullable NSDictionary<NSString*, id> *)change
context:nil
//如果收到的通知经过判断不是自己要观察的,则将该情况交给父类处理,因为父类也有可能使用了KVO
//[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];最后,移除观察者
1
- (void)removeObserver:self forKeyPath:@“name”;
KVO的原理
当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中被观察属性的 setter 方法,在setter方法里使其具有通知机制。同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。
在重写的
setter
里,给属性赋值的前后分别调用了两个方法:1
2- (void)willChangeValueForKey:(NSString *)key; //赋值前
- (void)didChangeValueForKey:(NSString *)key; //赋值后在
didChangeValueForKey:(NSString *)key
方法中会调用:1
2
3
4- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary<NSString*, id> *)change
context:nil
KVO使用注意
只有使用属性的setter方法,或通过key-path来设置属性值,观察者对象才会获得通知
1
2
3
4
5
6
7
8常见的几种设置方式:
1.遵循使用属性的setter方法
self.name = @"changed";
[self setName:@"changed"];
2.通过key-path设置
[self setValue:[NSString stringWithFormat:@"changed"] forKey:@"name"];观察者在使用结束后一定要在dealloc中移除,否则会导致资源泄漏
通知方法(observeValueForKeyPath)中,change字典保存了属性变更的信息
1
2NSLog(@"the old value is %@", [change objectForKey:@"old"]); //旧值
NSLog(@"the new value is %@", [change objectForKey:@"new"]); //新值