本质(更新于 2019年02月26日23:17:19):
首先,KVO是观察者模式的一种实现
其次,Apple使用了isa(is-a)混写技术,拖runtime的福,在运行时,系统生成了子类NSKVONotifying_SuperClass,然后将isa指针指向子类并重写setter方法,在setter方法上下添加了 willChangeValueForKey 与 didChangeValueForKey方法,所以我们自己也可以手动触发KVO
介绍:
- key-value-observing
- “观察者”设计模式的一种
- 每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了
举例:
还是拿Student类举例(为毛老是拿学生类举例呢,我也是很郁闷呢(⊙﹏⊙))
这个类就一个name属性(只读)
一个giveMeANewName方法,此方法直接操作_name变量
@interface Student : NSObject
@property (nonatomic, copy,readonly) NSString *name;
@end
#import "Student.h"
@implementation Student
- (NSString*)description {
NSDictionary* dic = @{
@"name":_name == nil ? @"" : _name,
};
return [NSString stringWithFormat:@"%@",dic];
}
- (void)giveMeANewName:(NSString*)name {
_name = name;
}
@end
在viewContent中:
@interface ViewController ()
@property (nonatomic, strong) Student *stu;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.stu addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
[self.stu giveMeANewName:@"MT"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
NSLog(@"%@",change);
}
- (Student*)stu {
if (!_stu) {
_stu = [Student new];
}
return _stu;
}
@end
运行后发现控制台什么也没有打印,说明这种方式并不能触发KVO
下面进行一些代码修改
- (void)viewDidLoad {
[super viewDidLoad];
[self.stu addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
[self.stu setValue:@"MT" forKey:@"name"];
}
这次用KVC的方式去改变属性值,运行
控制台打印如下信息,这就说明KVC可以触发KVO
如果我们不想用KVC去设置属性值,但又想监听只读属性的变化该怎么处理呢,这里提供一种方式:
在类的扩展中把只读属性改为readwrite
上面代码继续修改:
只读属性name在.m文件中改写为readwrite,giveMeANewName不再直接操作变量而是操作属性(调用set方法)
@interface Student()
@property (nonatomic, copy,readwrite) NSString *name;
@end
- (void)giveMeANewName:(NSString*)name {
self.name = name;
}
再次运行:
控制台打印如下信息,这就说明这种方式也可以触发KVO
希望会给大家带来帮助(o)/~