博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

01-02 iOS kvo、kvc

Posted on 2020-11-23 22:31  肖无情  阅读(69)  评论(0编辑  收藏  举报
#import "ViewController.h"
#import "FQPeople.h"
@interface ViewController ()

@property(nonatomic,strong) FQPeople *people;
@property(nonatomic,strong) FQPeople *people1;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.people = [FQPeople new];
    self.people1 = [FQPeople new];
    self.people1.age = 20;
    self.people.age = 20;
    [self.people addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"%@",@"这里被执行了");
    self.people.age = 21;
    [self.people1 setAge:22];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString: @"age"] && object == self.people) {
        NSLog(@"%@",change);
    }
}

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

1.1、引入

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.people.age = 21;
    [self.people1 setAge:22];
}

这两个方法都调用了People类的setAge方法。为什么people监听会被调用,people1不会呢?

setter方法是一样的,那就是people对象有不一样的地方, 接着往下看

1.2、isa的指向

不使用kvo (没有注册监听的时候)poeple 的isa指针指向的原来的类,即:WQPeople

使用kvo

在runtime创建了一个MJPerson的子类新类NSKVONotifying_MJPerson,并把poeple对象的isa指针指向 NSKVONotifying_MJPerson的类对象,

self.p1 = [[People alloc] init];
self.p2 = [[People alloc] init];
    
[self.p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

NSLog(@"%@",object_getClass(self.p1));//NSKVONotifying_People
NSLog(@"%@",object_getClass(object_getClass(self.p1)));//People
NSLog(@"%@",object_getClass(object_getClass(object_getClass(self.p1))));//NSObject


NSLog(@"%@",object_getClass(self.p2));//People
NSLog(@"%@",object_getClass(object_getClass(self.p2)));//People
NSLog(@"%@",object_getClass(object_getClass(object_getClass(self.p2))));//People

1.3、方法的调用流程

没有使用kvo:

self.p1 = [[People alloc] init];
self.p2 = [[People alloc] init];
    
[self.p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

p2,没有使用kvo,没有产生新的类,直接使用p2的isa指针找到p2的类对象,底层是objc_sendMsg() 这个流程访问原来的People中的setAge

使用了kvo runtime产生了新的类,

先p1的isa 找到People的类对象 找到NSKVONotifying_FQPeople 的setAge 在调用这个setAge时,调用的是fundation框架的 _NSSetIntValueAndNotify()

_这个_NSSetXXXValueAndNotify() 执行

  • willChangeValueForKey

  • 父类原来的setter进行真正的赋值

  • didChangeValueForKey;

    这里面会触发observe监听器的observeValueForKeyPath:ofObject:change方法。

那么setAge 和NSSetXXXValueAndNotify怎么关联的呢?

执行setAge时它的方法指针(IMP)直接指向了NSSetXXXValueAndNotify

 [self.p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

///获取imp
[self.p1 methodForSelector:@selector(setAge:)];

大概实现是

2 通过kvc获取值的过程,原理是什么?