iOS -KVO总结

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;
}

KVO原理

#import "NSObject+CLKVO.h"
#import <objc/message.h>
@implementation NSObject (CLKVO)
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context{
    //动态创建一个类
    NSString *oldClass = NSStringFromClass(self.class);
    NSString *newClass = [@"CLKVO_" stringByAppendingString:oldClass];
    Class MyClass = objc_allocateClassPair(self.class, newClass.UTF8String, 0);
    objc_registerClassPair(MyClass);
//    2,添加setName方法,重写
    class_addMethod(MyClass, @selector(setName:), setName, "");
//    3,修改类型
    object_setClass(self, MyClass);
    
}
//重写
void setName(){
    NSLog(@"重写了");
}
@end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力修福报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值