KVO知识点

1、kvo的简单使用

创建文件MJPerson类,添加属性age、height。

#import "ViewController.h"
#import "MJPerson.h"

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

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.person1 = [[MJPerson alloc] init];
    self.person1.age =10;
    self.person1.height=100;
    
    self.person2 = [[MJPerson alloc] init];
    self.person2.age = 9;
    self.person2.height = 90;
    
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld;
   
    //我们给person1加上kvo监听
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"haha"];
    [self.person1 addObserver:self forKeyPath:@"height" options:options context:nil];
    
      //我们不给person2加上kvo监听
//    [self.person2 addObserver:self forKeyPath:@"age" options:options context:nil];
//    [self.person2 addObserver:self forKeyPath:@"height" options:options context:nil];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    //person1和person2的age属性都发生变化
    self.person1.age = 20;
    self.person2.age = 99;
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    NSLog(@"监听到%@的属性%@发生了变化----%@",object,keyPath,change);
}

-(void)dealloc{
    //记得在适当的时候去除监听
    [self.person1 removeObserver:self forKeyPath:@"age"];
    [self.person1 removeObserver:self forKeyPath:@"height"];
    
   // [self.person2 removeObserver:self forKeyPath:@"age"];
   // [self.person2 removeObserver:self forKeyPath:@"height"];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end


=====================================================
打印结果为:
监听到的属性age发生了变化----{
    kind = 1;
    new = 20;
    old = 10;
}

因为我们去掉了对person2的监听,所以不会有显示person2属性的改变,但是person1的属性age改变被监听了下来。字典change的内容表示改变内容。

2、探究KVO本质

先上example:
创建文件MJPerson类,添加属性age。

#import "ViewController.h"
#import "MJPerson.h"

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

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.person1 = [[MJPerson alloc] init];
    self.person1.age =10;
    
    self.person2 = [[MJPerson alloc] init];
    self.person2.age = 9;

    //给person1加上kvo监听
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:nil];
}


-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//   因为使用了KVO监听self.person1,
//   所以,runtime动态创建一个新的类NSKVONotifying_MJPerson,这个类应该是MJPerson的子类
//   self.person1.isa = NSKVONotifying_MJPerson
    [self.person1 setAge:20];
    
    
//   self.person2.isa = MJPerson
    [self.person2 setAge:99];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    NSLog(@"监听到%@的属性%@发生了变化----%@",object,keyPath,change);
}

-(void)dealloc{
    [self.person1 removeObserver:self forKeyPath:@"age"];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

原理图如下:


31538461242_.pic.jpg
41538461242_.pic.jpg

伪代码如下:

@implementation NSKVONotifying_MJPerson

-(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];
}
@end
3、总结:

<1>KVO的本质是什么?
在程序运行中,利用runtime动态生成一个子类NSKVONotifying_MJPerson(该类是MJPerson的子类)且让对象的isa指向这个全新的子类。通过重写该子类set方法,使得修改对象的属性时,会调用foundation的_NSSetXXXValueAndNotify函数。

  • willChangeValueForKey
  • 父类原来的set方法
  • didChangeValueForKey
    继而触发监听器

<2>如何手动触发KVO?
本来是应该通过改变对象属性值来触发KVO的。如果非要手动触发,可以调用willChangeValueForKey和didChangeValueForKey方法,这样属性值没有发生变化,还能触发KVO监听。

<3>.直接修改成员变量会触发KVO吗?
只有通过触发set方法才会触发KVO,如果使用self.person1->_age这种方法来直接修改对象属性的话,是不会触发对象的set方法的,所以不会触发KVO监听。

你可能感兴趣的:(KVO知识点)