KVO学习

2018-11-08  本文已影响0人  朝夕向背

KVO,俗称键值监听,可以用于监听某个对象属性值的改变。

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

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

NS_ASSUME_NONNULL_END

#import "Person.h"

@implementation Person
- (void)setAge:(int)age{
    _age = age;
}
@end
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) Person *person1;
@property (nonatomic, strong) Person *person2;
@end

@implementation ViewController

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

- (void)viewDidLoad {
    [super viewDidLoad];
    self.person1 = [[Person alloc] init];
    self.person1.age = 20;

    self.person2 = [[Person alloc] init];
    self.person2.age = 19;

    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"height" options:options context:nil];
    NSLog(@"person1添加KVO监听之后-%p",[self.person1 methodForSelector:@selector(setAge:)]);
}

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

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

点击手机屏幕,打击结果如下

2018-11-07 15:08:56.214085+0800 KVO[5647:212323] age <Person: 0x6000016739c0> {
    kind = 1;
    new = 22;
    old = 20;
}

在touchesBegan方法内,相当于调用Person类的setAge方法,但是person1调用setAge方法后,又调用observeValueForKeyPath方法、而person2只是调用了setAge方法。由此可见,KVO的调用本质与setAge无关,而是与person1这个实例对象有关。我们在self.person12.age = 23;处打断点,打印后发现person1继承自NSKVONotifying_Person。而person2继承自Person。打印如下:

(lldb) p self.person1.isa
(Class) $0 = NSKVONotifying_Person
  Fix-it applied, fixed expression was: 
    self.person1->isa
person2继承关系图
person1继承关系图

底层调用情况

self.person1.age = 22处打断点,点击屏幕之后,调出lldb,对person1添加KVO监听之后打印的地址值输出p IMP(0x105e51cf2),打印出结果

p IMP(0x105e51cf2)
(IMP) $0 = 0x0000000105e51cf2 (Foundation _NSSetIntValueAndNotify)

由此说明调用被监听的类中的setAge方法,这个方法内部调用C语言函数_NSSetIntValueAndNotify

下面通过伪代码来说明子类NSKVONotifying_A内部的调用(不能运行)。

#import "NSKVONotifying_Person.h"

@implementation NSKVONotifying_Person
- (void)setAge:(int)age{
    _NSSetIntValueAndNotify();
}
void _NSSetIntValueAndNotify(){
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

- (void)didChangeValueForKey:(NSString *)key{
    [self observeValueForKeyPath:key ofObject:self change:nil context:nil];
}
@end

NSKVONotifying_Person内部重写setAge方法。当age属性值改变时:

上一篇 下一篇

猜你喜欢

热点阅读