- 1 . KVO 的基本使用
kvo "键值监听", 全称"Key-Value Observing" ,监听对象属性值的改变
// 监听对象
@interface Person : NSObject
// 监听属性
@property (nonatomic,copy)NSString * name;
@end
@implementation Person
@end
//----------------------------
@interface ViewController : UIViewController
@end
@interface ViewController ()
// 创建对象
@property (nonatomic,strong)Person *person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [Person new];
self.person.name = @"jack";
// 配置监听
NSKeyValueObservingOptions options = NSKeyValueObservingOptionOld |NSKeyValueObservingOptionNew;
[self.person addObserver:self forKeyPath:@"name" options:options context:nil];
}
// 触发监听
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.person.name = @"rose";
}
// 监听调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"keyPaht ::%@ ; object ::%@ ; change ::%@ ;context ::%@ ",keyPath,object,change,context);
}
// 移除监听
- (void)dealloc
{
[self.person removeObserver:self forKeyPath:@"name"];
}
@end
/*
keyPaht ::name ;
object ::<Person: 0x600003bfc340> ;
change ::{
kind = 1;
new = rose;
old = jack;
} ;
context :: (null)
*/
- 2 . kvo 的本质
a. 属性值的改变,其本质是调用内部的set 方法;
b. 利用runtime,动态生成了 NSKVONotifying_Person 类 继承于 Person类(NSKVONotifying_Person superclass 指向 person),
同时修改person 对象的isa 指向了 NSKVONotifying_Person 类 复写set 方法,class 方法
(NSKVONotifying_Person 有自己的元类对象 ,所以 类对象的isa 指向自己的元类对象)
c. 在set 方法中调用 willchange 等方法;
@interface NSKVONotifying_Person : Person
@end
/*新增
_isKVOA
dealloc
class // return 【Person class】
setName //调用Foundation 下的 _NSSetXXXValueAndNotify(c 函数)
*/
伪代码
@implementation NSKVONotifying_Person
-(void)setName(NSString *)name{
_NSSetXXXValueAndNotify()
}
void _NSSetIntValueAndNotify() {
[self willChangeValueForKey:@"name"];
[super setName:@"xx"];// 调用父类(person 类修改person 的值)
[self didChangeValueForKey:@"name"];
}
-(void)didChangeValueForKey(NSString *)name{
【observer observeValueForKeyPath... 】
}
@end
- 3 .手动触发 kvo
// 手动调用
- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;
//因为在 didChange方... 法中判断 willChange... 是否有调用 ,所以要先调用willChange...
- 4 . 触发kvo的前提:对象的属性 必须有 set 方法;
@interface Person : NSObject{
int age;
}
@property (nonatomic,copy)NSString * name;
@end
// -----------------
@interface ViewController ()
@property (nonatomic,strong)Person *person;
@end
@implementation ViewController
- (void)viewDidLoad {
self.person = [Person new];
self.person.name = @"jack";
self.person->age = 12;
NSKeyValueObservingOptions options = NSKeyValueObservingOptionOld |NSKeyValueObservingOptionNew;
[self.person addObserver:self forKeyPath:@"name" options:options context:nil];
[self.person addObserver:self forKeyPath:@"age" options:options context:nil];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.person.name = @"rose";
self.person->age = 13;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"keyPaht ::%@ ; object ::%@ ; change ::%@ ;context ;;%@ ",keyPath,object,change,context);
}
- (void)dealloc
{
[self.person removeObserver:self forKeyPath:@"name"];
}
/*
这里只能监听到name属性 ; 无法监听 age 属性;
因为age 并没有set 方法,所以不会触发监听;
*/
@end
- 5 . 属性和成员变量得区别
可以参照这篇博客