观察者模式
Observer 观察者模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在更改其状态时,会通知所有的观察者对象,使他们能够自动更新自己。一个主题对象可以对应多个观察者,而这些观察者之前没有相互联系,可以根据需要增加和删除观察者,使得系统更易于拓展。
NSNotificationCenter
NSNotificationCenter 和 NSNotificationiOS 中的通知机制是非常典型的观察者模式,所有的类可以通过 NSNotificationCenter 监听和发送 NSNotification,观察者和被观察者都无需知晓对方,只需要通过标记(例如 NotificationName)在 NSNotificationCenter 中找到监听该通知所对应的类,从而调用该类的方法。并且,在 NSNotificationCenter 中,观察者可以只订阅某一特定的通知,并对其做出响应,而不用对某一个类发送的所有通知都进行更新操作。NSNotificationCenter 对观察者的调用不是随机的,而是遵循注册顺序一一执行,并且在该线程内是同步的。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notice:) name:@"tongzhi" object:nil];
-(void)notice:(id)sender{
NSLog(@"%@",sender);
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"tongzhi" object:nil];
使用对象来发消息
//发通知
-(void)btn2Click:(UIButton *)btn
{
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti2" object:[NSString stringWithFormat:@"%@",btn.titleLabel.text]];
}
//监听
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(noti2:) name:@"noti2" object:nil];
//调用方法
-(void)noti2:(NSNotification *)noti
{
//使用object处理消息
NSString *info = [noti object];
NSLog(@"接收 object传递的消息:%@",info);
}
使用userinfo来发消息
//发通知
-(void)btn3Click
{
NSDictionary *dic = [NSDictionary dictionaryWithObject:@"userInfo消息" forKey:@"param"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti3" object:nil userInfo:dic];
}
//监听
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(noti3:) name:@"noti3" object:nil];
//调用方法
-(void)noti3:(NSNotification *)noti
{
//使用userInfo处理消息
NSDictionary *dic = [noti userInfo];
NSString *info = [dic objectForKey:@"param"];
NSLog(@"接收 userInfo传递的消息:%@",info);
}
和delegate相比,是更大跨度的通讯机制。
KVO是什么
KVO: 全称 Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变。
如图所示,当被监听者的被监听内容发生变化时,会调用监听者的observeValueForKeyPath
我们打个断点,看看添加kvo前后对象的变化。
实例对象的isa指针指向父类,添加了观察者后,从Person变成了NSKVONotifying_Person.
我们重写set方法,查看调用栈
没有添加观察者
添加了观察者后:
先调用的是这个NSSetObjectValueAndNotify函数,而我们打印setString的地址
NSLog(@"%p", [person methodForSelector:@selector(setString:)]);
然后取它的imp
也就是说,添加了监听的set方法实现变成了这个。
因为我们是为对象添加了监听,其实还有许多方法
既然它重写了set方法,那么它会不会也重写了其他方法
Class cls = object_getClass(person);
unsigned int count ;
Method *methods = class_copyMethodList(cls, &count);
NSMutableString *methodNames = [NSMutableString string];
[methodNames appendFormat:@"%@ - ", cls];
for (int i = 0 ; i < count; i++) {
Method method = methods[i];
NSString *methodName = NSStringFromSelector(method_getName(method));
[methodNames appendString: methodName];
[methodNames appendString:@" "];
}
NSLog(@"%@",methodNames);
free(methods);
打印结果:
NSKVONotifying_Person - setString: class dealloc _isKVOA
它把class也重写了,所以我们调用class方法返回的是Person而不是上面提到的类。
我们猜测它修改了class方法是:
- (Class) class {
// 得到类对象,在找到类对象父类
return class_getSuperclass(object_getClass(self));
}
因为实例调用superclass会先调用class方法再调用superclass,所以我们打印superclass是NSObject。
在此,NSKVONOtifying_Person应该是Person的子类。
原博客图片:
_NSSetIntValueAndNotify
验证didChangeValueForKey:内部会调用observer的observeValueForKeyPath:ofObject:change:context:方法
我们在Person类中重写willChangeValueForKey:和didChangeValueForKey:方法,模拟他们的实现。
willChangeValueForKey: - begin
2020-08-11 21:20:32.040307+0800 KVO探索[6736:18622244] willChangeValueForKey: - end
2020-08-11 21:20:32.040396+0800 KVO探索[6736:18622244] setString
2020-08-11 21:20:32.040475+0800 KVO探索[6736:18622244] didChangeValueForKey: - begin
2020-08-11 21:20:32.040595+0800 KVO探索[6736:18622244] success
先调用了willChangeValueForKey 然后是set方法,然后调用didChangeValue,然后调用observeValue方法。
手动触发KVO
手动调用willChangValueForKey和didChangeValue。