iOS:KVO

KVO简介

KVO,是一个非正式协议,提供了一个途径,使对象(观察者)能够观察其他对象(被观察者)的属性,当被观察者的属性发生变化时,观察者就会被告知该变化。由NSKeyValueObserving协议提供支持,NSObject类继承了该协议,所以NSObject的子类都可使用该方法。

基本方法

添加观察者

/*
object : 被观察对象
observer: 观察对象
forKeyPath里面带上property的name,如UIView的frame、center,如自定义对象的成员变量等等
options: 有4个值,分别是:
NSKeyValueObservingOptionNew 把更改之后的值提供给处理方法
NSKeyValueObservingOptionOld 把更改之前的值提供给处理方法
NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后。
context: 可以带入一些参数,其实这个挺好用的,任何类型都可以,自己强转就好了
*/
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context

实现观察者回调方法

/*
keyPath: 对应forKeyPath
object:  被观察的对象
change:  对应options里的NSKeyValueObservingOptionNew、NSKeyValueObservingOptionOld等
context: 对应context
*/
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary*)change context:(nullable void *)context

移除观察者

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath

KVO模式

KVO分为自动和手动两种模式
简单的实现一个自动模式

//自动模式
- (void)viewDidLoad {
    [super viewDidLoad];
    Person *person = [[Person alloc] init];
    person.name = @"A";
    //NSLog(@"class:%s",object_getClassName(person));
    NSLog(@"before:%@",[self findSubClass:[person class]]);
    [person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    NSLog(@"before:%@",[self findSubClass:[person class]]);
    //NSLog(@"class:%s",object_getClassName(person));
    person.name = @"B";
}

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

/*
利用runtime获取该类的所有子类
*/
- (NSArray *)findSubClass:(Class)defaultClass {
    int count = objc_getClassList(NULL,0);
    if (count <= 0) {
        return [NSArray array];
    }
    NSMutableArray *output = [NSMutableArray arrayWithObject:defaultClass];
    Class *classes = (Class *)malloc(sizeof(Class) * count);
    objc_getClassList(classes, count);
    for (int i = 0; i < count; i++) {
        if (defaultClass == class_getSuperclass(classes[i])) {
            [output addObject:classes[i]];
        }
    }
    return output;
}
image.png

实现一个手动模式,首先要明白KVO自动模式的实现原理:
如果原类为 Person,那么会自动生成一个继承Person类的派生类,名为 NSKVONotifying_Person (见自动模式截图),系统会偷偷将原类Person的isa 指针指向动态生成的派生类,在给被监控属性赋值时执行的其实是派生类的 setter 方法,该派生类的setter方法中会增加willChangeValueForKey和didChangeValueForKey方法。

//手动模式
/*
需要在被观察者对象(这里被观察者是Pesron类)中重写automaticallyNotifiesObserversForKey静态方法
在这里,我仅针对成员变量name进行手动
*/
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    if ([key isEqualToString:@"name"]) {
        return NO;
    }
    return YES;
}

/*
手动模式就需要手动调用willChangeValueForKey和didChangeValueForKey两个方法
*/
- (void)setName:(NSString *)name {
    [self willChangeValueForKey:@"name"];
    _name = name;
    [self didChangeValueForKey:@"name"];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    Person *person = [[Person alloc] init];
    person.name = @"A";
    [person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    person.name = @"B";
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    NSLog(@"%@",change);
    NSLog(@"class:%@",[object class]);
}

你可能感兴趣的:(iOS:KVO)