iOS -KVO

2021-03-11  本文已影响0人  CDLOG

KVO-键值观察

KVC:对象取值或者设置值。
KVO:监听对象值的变化。

响应式编程的一种。
KVO的使用非常简单,使用KVO的要求是对象必须能支持kvc机制——所有NSObject的子类都支持这个机制。
KVO观察的实际是属性的setter方法,成员变量的改变不能被观察到。

KVO的实现原理如下

在调用addObserver方法的时候,实现了以下步骤
1,利用runtime动态创建当前类的子类。
2,重写子类的setter方法,并在内部回复父类的做法。
3,动态修改当前类的类型,让他变成子类的类型。(以后调用setter方法,实际是调用子类的setter方法)。

1,简单的例子,观察person类的name属性的变化

@interface ViewController ()
@property (nonatomic, strong) PersonModel * person;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"load");
    self.person = [[PersonModel alloc]init];
    //添加观察者
    [self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    
    
}
//观察回调
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSLog(@"%@",change);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    static int a = 0;
    self.person.name = [NSString stringWithFormat:@"%d",a++];
}

//移除观察者
-(void)dealloc{
    [self.person removeObserver:self forKeyPath:@"name"];
}
@end

2,手动触发KVO,在需要的时候才触发,默认是自动触发

在需要手动触发的类重写类方法。
关闭自动触发

#import "PersonModel.h"

@implementation PersonModel

//是否自动触发观察key的改变
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
    //name属性手动触发,其他属性自动触发
    if([key isEqualToString:@"name"]){
        return NO;
    }
    return YES;
}

@end

手动触发

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    static int a = 0;
    //即将触发
    [self.person willChangeValueForKey:@"name"];
    self.person.name = [NSString stringWithFormat:@"%d",a++];
    //已经触发
    [self.person didChangeValueForKey:@"name"];
}

3,类的属性是对象时添加观察的方式。
person中包含了一个dog对象,观察dog的age。

//添加观察者
 [self.person addObserver:self forKeyPath:@"dog.age" options:NSKeyValueObservingOptionNew context:nil];

4,数组,字典等容器类,观察不到,因为没有setter方法。但是可以利用KVO观察容器属性的变化
实际原理是重写了集合的添加方法,删除方法,替换方法,原理和重写setter方法是一样的。

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //利用KVC获取到arr,注意这里使用的是mutableArrayValueForKey,数组直接初始化数据可以不用KVC。
    NSMutableArray *arr = [self.person mutableArrayValueForKey:@"arr"];
    //KVC可以监听到数组的添加,删除,替换
    [arr addObject:@"11"];
    
    
}

5,添加一个类的子类的多个属性观察,注意这里子类前要有一个_,也可以直接添加多个观察
VC.m

[self.person addObserver:self forKeyPath:@"dog" options:NSKeyValueObservingOptionNew context:nil];

PersonModel.m

//返回要观察的字符串
+(NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
    NSSet *keypath = [NSSet set];
    if([key isEqualToString:@"dog"]){//子类属性名
        keypath = [keypath setByAddingObjectsFromArray:@[@"_dog.name",@"_dog.age"]];
    }
    return keypath;
}
上一篇下一篇

猜你喜欢

热点阅读