Skip to main content
  1. Objective-C/

KVO (Key-Value Observing)

·1 分钟
KVO OC
Objc - 系列文章之一
Part 12: 当前阅读
KVO是利用runtime的特性动态生成观察对象类的子类,然后重写被观察对象的属性的set方法。

可以用于监听某个对象属性值的改

// 如何设置属性的KVO
@property (nonatomic, strong) Person *person1;
- (void)viewDidLoad {
  [super viewDidLoad];
  _person1 = [[Person alloc] init];
  _person1.age = 32;
  // 需要返回的数据,新的值 | 旧的值
  NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
  // 对person1属性age设置监听,附带信息:@"附加信息"
  [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"附加信息"];
}
// person1.ag改变时进行调用这里
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
  NSLog(@"keyPath: %@, object: %@;\nchange: %@, context: %@", keyPath, object, change, context);
}
// 监听者销毁时移除相应的监听
-(void)dealloc {
  [_person1 removeObserver:self forKeyPath:@"age"];
}
KVO的本质(内部实现)>

KVO的本质(内部实现) #

打印对象isa地址、name类名>

打印对象isa地址、name类名 #

在设置监听前后打印person1,2的isa地址。发现person1在设置完监听后地址发生了改变;在设置监听前后打印person1,2的类名。发现person1在设置完监听后改变为:NSKVONotifying_Person;

// person1->isa: 0x10bcb0710
NSLog(@"person1->isa: %p", object_getClass(_person1));
// person2->isa: 0x10bcb0710
NSLog(@"person2->isa: %p", object_getClass(_person2));
// person1 class name: Person
NSLog(@"person1 class name: %p", object_getClass(object_getClass(_person1)));
// person2 class name: Person
NSLog(@"person2 class name: %p", object_getClass(object_getClass(_person2)));

NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.person1 addObserver:self forKeyPath:@"age" options:options context:@"附加信息"];

// person1->isa: 0x6000022a03f0
NSLog(@"person1->isa: %p", object_getClass(_person1));
// person2->isa: 0x10bcb0710
NSLog(@"person2->isa: %p", object_getClass(_person2));
// person1 class name: NSKVONotifying_Person
NSLog(@"person1 class name: %p", object_getClass(object_getClass(_person1)));
// person2 class name: Person
NSLog(@"person2 class name: %p", object_getClass(object_getClass(_person2)));

// person1 class`s class name: Person
NSLog(@"person1 class`s class name: %@", object_getClass(object_getClass(person1)).superclass);
小结>

小结 #

可以看出,程序在runtime的时候动态生成了 NSKVONotifying_PersonNSKVONotifying_Person 继承于 Person,当改变 age 的值时,即调用 setAge: 方法。NSKVONotifying_Person 重写了 setAge:并做了其他事情。具体做了那些事可以猜测:

- (void)setAge:(int)age {
  [_person1 willChangeValueForKey:@"age"];
  [super setAge:age];
  [_person1 didChangeValueForKey:@"age"];
}

didChangeValueForKey:内部会调用observerobserveValueForKeyPath:ofObject:change:context:方法

即:KVO是利用runtime的特性动态生成观察对象类的子类,然后重写被观察对象的属性的set方法。
如何手动触发KVO?>

如何手动触发KVO? #

手动调用willChangeValueForKey:didChangeValueForKey: 。只调用didChangeValueForKey: 是无法触发的。

使用场景>

使用场景 #

  • 实现上下拉刷新控件 content offset
  • webview 混合排版 content size
  • 监听模型属性实时更新UI
  • NSOperation
  • NSoperationQueue
  • RAC


Objc - 系列文章之一
Part 12: 当前阅读