JHHK

欢迎来到我的个人网站
行者常至 为者常成

KVO

目录

基本使用

KVO的全称是Key-Value Observing,俗称键值监听,可以用于监听某个对象属性值的改变。

创建一个Person类

@interface Person : NSObject
@property (assign, nonatomic) int age;
@end

使用案例

#import "ViewController.h"
#import "Person.h"

@interface ViewController ()
@property (nonatomic, strong) Person * person1;//
@property (nonatomic, strong) Person * person2;//
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person1 = [[Person alloc] init];
    self.person1.age = 1;
    
    self.person2 = [[Person alloc] init];
    self.person2.age = 2;
    
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:(options) context:@"123"];
}

-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                      context:(void *)context{
    
    NSLog(@"object=%@",object);
    NSLog(@"change=%@",change);
    NSLog(@"context=%@",context);
}


-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.person1.age = 10;
    self.person2.age = 20;
}

-(void)dealloc{
    //一定要移除监听,否则引起内存泄漏
    [self.person1 removeObserver:self forKeyPath:@"age"];
}
@end

每次点击屏幕的打印日志为

object=<Person: 0x13565c030>
change={
    kind = 1;
    new = 10;
    old = 2;
}
context=123

分析

如上面的例子,我们在添加监听前打个断点,添加监听后打个断点,来看看person1和person2发生了什么变化。

//添加监听前:lldb调试
(lldb) po self.person1->isa
Person

(lldb) po self.person2->isa
Person

//添加监听后:lldb调试
(lldb) po self.person1->isa
NSKVONotifying_Person

(lldb) po self.person2->isa
Person

可以看出添加了监听的person1的isa指针的指向发生了变化。产生了一个新的类对象,NSKVONotifying_Person。

我们来分析下添加监听后person1->isa指向的类对象NSKVONotifying_Person。

工程中引入 NSObject+classInfo这个分类。NSObject+classInfo

#import "NSObject+classInfo.h"

//添加监听后调用
Class KVO_Person = object_getClass(self.person1);
[KVO_Person printInfoOfClass];

//打印的日志如下
----------- protocol -----------

----------- iVar -----------

----------- property -----------

----------- instanceMethod -----------
instanceMethodName-0 = setAge:
instanceMethodName-1 = class
instanceMethodName-2 = dealloc
instanceMethodName-3 = _isKVOA

----------- classMethod -----------

通过以上分析可知:

(监听前)perosn1->isa 指向 Person类对象 (监听后)person1->isa 指向 NSKVONotifying_Person类对象
setAge: setAge:
age class
  dealloc
  _isKVOA

下面再来看下的KVO_Person的superclass指向哪里,可以看出 KVO_Person 的父类是Person类对象。

(lldb) p/x [KVO_Person superclass]
(Class) $2 = 0x0000000100059f18 Person
(lldb) p/x [Person class]
(Class) $3 = 0x0000000100059f18 Person

img

NSKVONotifying_Person

#import "NSKVONotifying_MJPerson.h"

@implementation NSKVONotifying_Person

- (void)setAge:(int)age{
    _NSSetIntValueAndNotify();
}

// 伪代码
void _NSSetIntValueAndNotify(){
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

- (void)didChangeValueForKey:(NSString *)key{
    // 通知监听器,某某属性值发生了改变
    [oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}

// 屏幕内部实现,隐藏了NSKVONotifying_MJPerson类的存在
- (Class)class{
    return [MJPerson class];
}

- (void)dealloc{
    // 收尾工作
}

- (BOOL)_isKVOA{
    return YES;
}

@end

补充

手动触发监听的方法

[self.person1 willChangeValueForKey:@"age"];
[self.person1 didChangeValueForKey:@"age"];

//或者
self.person1.age = self.person1.age;

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.